> Agent-readable docs index: /llms.txt. Download /docs.zip to grep all markdown files locally.

---
$schema: https://holocron.so/frontmatter.json
title: Targets and Generated C
description: Connect generated C to hardware through I/O hooks, Modbus, EtherCAT, ROS 2, and retained state.
icon: lucide:cpu
---

<Aside full>
  <Warning>
    **`rbcpp_target` is not a safety-certified robot controller.** Safety gating, watchdogs, and transport bridges are experimental helpers for integration testing, not certified machinery safety.
  </Warning>
</Aside>

`rbcpp build-c` emits **portable C** with a scan loop, program state, and target-facing hooks. The **`rbcpp_target`** crate supplies HAL adapters you wire to that ABI during deployment experiments.

## Generated C ABI

Each program gets a state struct and functions such as:

Generated files begin with an **SPDX header** (`SPDX-License-Identifier: MIT OR Apache-2.0`). See [License](/license).

| Symbol                                                       | Role                                                                                 |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| `{program}_init`                                             | Cold start: initialize all state                                                     |
| `{program}_warm_restart`                                     | Warm start: reset non-retained locals, preserve RETAIN                               |
| `{program}_scan`                                             | One deterministic scan cycle                                                         |
| `{program}_set_target_hooks`                                 | Install I/O, retain, time, and scan lifecycle callbacks                              |
| `{program}_load_retained` / `{program}_save_retained`        | Persist `RETAIN` variables through target storage                                    |
| `{program}_set_comm_hook`                                    | Bridge standard communication FBs to a transport                                     |
| `{program}_read_access_path` / `{program}_write_access_path` | Service `VAR_ACCESS` paths with `READ_ONLY` enforcement                              |
| `rbcpp_io_symbols[]`                                         | Names, IEC locations, directions, C types, and sizes for `%I` / `%Q` / `%M` bindings |

The **`rbcpp_target_hooks`** table includes `io_read`, `io_write`, `retain_load`, `retain_save`, `time_ms`, `begin_scan`, `end_scan`, and `watchdog_pet`. The scan loop calls these hooks so located symbols and retained state stay outside the generated IEC lowering.

### Cycle time in generated C

Generated headers define **`#define RBCPP_CYCLE_MS 1`**. Timer FB elapsed fields, SFC timed qualifiers, and similar logic increment by this constant each scan unless your target **`time_ms`** hook supplies a different notion of time. This is independent of **`RuntimeOptions.cycle_time_ms`** in the Rust interpreter unless you align them deliberately.

### Embedded C runtime helpers

Generated C includes a portable runtime layer beyond scan logic: 64-bit shift/rotate helpers, UTF-8 string indexing, BCD conversions, civil date/time construction and parsing (`rbcpp_concat_dt`, `rbcpp_string_to_dt`, and related symbols), communication request/response shims (`rbcpp_comm_request`, `rbcpp_comm_response`), and SFC action-control state. Interpreter and generated C paths are kept in parity through shared regression tests.

```bash
cargo run -p rbcpp_cli -- build-c examples/counter.st -o build/counter.c
```

## `rbcpp_target` overview

All helpers are **stdlib-only Rust**. They implement the shared **`TargetHal`** trait so you can stack adapters (for example **`SafetyHal`** wrapping **`ModbusHal`**) or route through **`TargetSupervisor`**.

| Component                                                   | Purpose                                                                                               |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `FileBackedHal`                                             | Map `%I`, `%Q`, `%M`, or symbol names to text files (`bool`, `decimal`, `text`)                       |
| `load_mapping`                                              | Parse comma-separated file mapping lines into a `FileBackedHal`                                       |
| `parse_ide_mapping`                                         | Parse Studio **`target/mapping.toml`** lines into file or Modbus bindings                             |
| `ModbusHal` / `ModbusImage`                                 | In-memory coil, discrete-input, holding-register, and input-register tables                           |
| `ModbusTransportHal`                                        | Same bindings backed by a **`ModbusTransport`** trait (wrap a real Modbus client)                     |
| `load_modbus_mapping` / `load_modbus_transport_mapping`     | Parse Modbus point strings from mapping files                                                         |
| `EtherCatHal` / `EtherCatPdoImage`                          | In-memory EtherCAT PDO process image with typed little-endian reads/writes                            |
| `EtherCatTransportHal`                                      | PDO bindings backed by an **`EtherCatTransport`** trait                                               |
| `load_ethercat_mapping` / `load_ethercat_transport_mapping` | Parse PDO point strings from mapping files                                                            |
| `Ros2Bridge`                                                | In-memory ROS 2 topic subscription, publication, and parameter bridge                                 |
| `Ros2TransportHal`                                          | Same bindings backed by a **`Ros2Transport`** trait                                                   |
| `load_ros2_mapping` / `load_ros2_transport_mapping`         | Parse ROS 2 point strings from mapping files                                                          |
| `AccessRuntime`                                             | Target-side **`VAR_ACCESS`** bindings to program state or external I/O                                |
| `TargetSupervisor`                                          | Coordinates HAL, access paths, retained load/save, watchdog, safety gate, and cycle reports           |
| `RetainStore`                                               | File-backed load/save for retained variable snapshots (canonical key filenames)                       |
| `CycleWatchdog`                                             | Detect scan loops that miss a deadline                                                                |
| `SafetyGate` / `SafetyHal`                                  | Non-certified output gating on E-stop, protective stop, watchdog expiry, fault latch, operator enable |

### File-backed I/O mapping

Mapping files are line-oriented CSV. Each non-comment line binds a **location or symbol key** to a file path and encoding:

```text
%IX0.0, inputs/motor_sensor.txt, bool
%QX0.0, outputs/motor_cmd.txt, bool
%MW10, memory/setpoint.txt, decimal
```

Encodings include **`bool`**, **`decimal`**, and **`text`**.

### Retained-state files

**`RetainStore`** writes one file per retained symbol under a root directory: **`{canonical_key}.retain`**, using the same uppercased key form as runtime traces. On load, it falls back to a **legacy flat filename** when the canonical path is missing but an older `{name}.retain` file exists (for example after a casing fix). Nested retained fields such as **`Counter.Cv`** map to their own canonical keys.

### Modbus points

`ModbusPoint::parse` accepts strings like `1:holding:42` (unit id, area, address). Areas include **coil**, **discrete**, **holding**, and **input** register families. Writable areas are coils and holding registers.

Example mapping file:

```text
%QX0.1, 1:coil:9
%MW2, 2:holding:12
Sensor, 3:discrete:7
```

Studio **`target/mapping.toml`** uses the same Modbus point strings through **`parse_ide_mapping`**.

### EtherCAT PDO points

`EtherCatPdoPoint::parse` accepts colon-separated fields:

```text
slave:index:subindex:area:byte.bit:type
```

| Field      | Examples                                                  |
| ---------- | --------------------------------------------------------- |
| `area`     | `input`, `output`, `rx`, `tx`, `pdo_in`, `pdo_out`        |
| `byte.bit` | `0.0` for a bool at byte 0 bit 0                          |
| `type`     | `bool`, `u16`, `i32`, `dint`, and related IEC-style names |

Example mapping file:

```text
Start, 1:0x6000:1:input:0.0:bool
%DQ0, 2:0x7000:2:output:2.0:i32
```

Use **`EtherCatPdoImage::write_input_for_simulation`** to inject input PDO values in tests before reads.

### ROS 2 bridge points

`Ros2Point::parse` accepts `direction:name`:

| Direction | Aliases                        | Behavior                                    |
| --------- | ------------------------------ | ------------------------------------------- |
| Subscribe | `sub`, `subscription`, `input` | Read topic values injected by the transport |
| Publish   | `pub`, `publisher`, `output`   | Queue outgoing messages on write            |
| Parameter | `param`, `parameter`, `cfg`    | Read/write named parameters                 |

Example mapping file:

```text
Battery, sub:/battery
%QX0.1, pub:/motor_enable
Speed, param:/speed_limit
```

## VAR\_ACCESS on the target side

**`AccessRuntime`** registers **`AccessPathBinding`** entries that map IEC access path names to either:

* **`AccessTarget::State`**: a retained or non-retained program variable key in **`TargetState`**
* **`AccessTarget::Io`**: an **`IoSymbol`** served through a **`TargetHal`**

Reads and writes enforce **`READ_ONLY`** vs **`READ_WRITE`**. I/O-backed paths require **`read_access_with_hal`** / **`write_access_with_hal`** (or **`TargetSupervisor`**, which routes through the installed HAL).

**`TargetSupervisor`** ties the pieces together for a generated scan loop:

1. **`cold_start`** / **`warm_start`**: load retained values, pet watchdog
2. **`begin_cycle`**: refresh safety state from **`SafetyInputs`**
3. Run generated **`{program}_scan`**
4. Service external **`VAR_ACCESS`** reads/writes through the HAL
5. **`end_cycle`**: save retained state, pet watchdog, emit **`TargetCycleReport`** (cycle time, watchdog expiry, safety state)

<Aside>
  <Tip>
    Use generated **`rbcpp_io_symbols`** metadata to connect hook callbacks to the correct C fields without hard-coding layout knowledge in your target package.
  </Tip>
</Aside>

## Target bridge (Studio integration)

**`rbcpp_target_bridge`** exposes a local HTTP API consumed by RoboC++ Studio:

| Endpoint                          | Role                                                   |
| --------------------------------- | ------------------------------------------------------ |
| `GET /health`                     | Bridge liveness                                        |
| `GET/POST/DELETE /api/v1/session` | Connect or disconnect Simulator or Modbus TCP hardware |
| `POST /api/v1/deploy`             | Stage a Studio deploy package                          |
| `GET/POST /api/v1/io`             | Read or write mapped symbols                           |
| `POST /api/v1/session/control`    | Run, stop, or reset the staged target session          |

Start from the repository root:

```bash
cargo run -p rbcpp_target_bridge -- --bind 127.0.0.1:8787
```

Or use **`npm run dev:with-target`** from **`ide/web`**. See [RoboC++ Studio](/studio) and **`ide/web/docs/TARGET_BRIDGE.md`** in the compiler repo.

## Safety gating (experimental)

**`SafetyGate`** tracks **`SafetyInputs`** (emergency stop, protective stop, operator enable, reset) and optional watchdog expiry. It blocks **output-direction** writes through **`SafetyHal`** or **`TargetSupervisor`** when:

* E-stop or protective stop is active
* The cycle watchdog expired
* A fault is latched until reset
* Operator enable is required but not active

These pieces are meant to be **composed and validated by the builder** for the target machine, line, robot cell, or product. Production deployments should validate timing, retained-state behavior, I/O failure behavior, operator-enable behavior, watchdog policy, and hardware mapping for their environment.

The compiler repo **`validation/deployment/`** directory ships integrator checklists (`target-validation-template.md`), simulator-in-the-loop guidance (`simulator-in-loop.md`), and a generated-C HAL example. Run **`cargo test -p rbcpp_target`** for adapter regression coverage.

## Communication function blocks

Standard communication FBs (`USEND`, `URCV`, `BSEND`, `BRCV`, `SEND`, `RCV`) expose `DONE`, `NDR`, `ERROR`, and `STATUS` in generated state. Install a **`_set_comm_hook`** handler to connect them to Modbus, EtherCAT, ROS 2, GPIO, or another transport without editing the scan function body.

## What is still planned

* Production HAL packages that wrap certified fieldbus clients and robot middleware stacks
* Full external binding for every `VAR_ACCESS` path across distributed runtimes
* Certified watchdog and cycle-time integration beyond experimental helpers
* Certified online-change and production target monitoring beyond the experimental Studio bridge

See [Compliance](/compliance) for `backend.c.target_abi` and [Architecture](/architecture) for crate boundaries.

## Related

* [CLI reference](/cli): `build-c`, `--access`
* [Execution model](/execution-model): simulator access-path injection
* [Types and variables](/types-and-variables): `VAR_ACCESS`, `RETAIN`, located variables
* [Architecture](/architecture): pipeline and crates


---

*Powered by [holocron.so](https://holocron.so)*
