# Configuration and limits

> Machine limits (HardConfig) and working limits (SoftConfig), base units, homing, limit switches and how the configuration is stored.

UPAN keeps two layers of configuration on the motion MCU. **HardConfig** describes the machine: mechanics, motor current, absolute travel and speed limits, limit switches and homing. **SoftConfig** holds the working limits for the current job and must stay inside HardConfig. Every motion command and every program step is checked against these limits on the device itself, whoever sent it — an app, a script or an AI agent. This page describes both layers, the units they use, homing and limit switch settings, and how the configuration is stored.

> **Note:** This page follows board revision A and firmware that is still in development. Field names come from the protocol schema; see [`HardConfig`](/docs/api#HardConfig) and [`SoftConfig`](/docs/api#SoftConfig) in the API reference.

## Base units

The protocol uses only metric integers, called base units. The axis type (`HardConfig.axis_kind`) decides which ones apply.

| Quantity | Linear axis (`AXIS_LINEAR`) | Rotary axis (`AXIS_ROTARY`) |
|---|---|---|
| Position, distance | µm | millidegree (m°) |
| Velocity | µm/s | m°/s |
| Acceleration | µm/s² | m°/s² |
| Time | ms | ms |

There are no inches in the protocol. Host tools convert exactly (1 in = 25 400 µm) before sending. `upan-cli` accepts `um`, `mm`, `cm`, `m`, `in` on a linear axis and `mdeg`, `deg`, `rev` on a rotary axis; a plain number means millimetres or degrees. `SoftConfig.display_units` stores whether a user interface should show metric or imperial values; it has no effect on motion.

In motion commands, a velocity or acceleration of `0` means "use the default from SoftConfig".

## Two layers

| | HardConfig | SoftConfig |
|---|---|---|
| Describes | the machine | the current job |
| Typical content | steps per revolution, travel per revolution, absolute travel, maximum speed and acceleration, motor current, limit switches, homing, encoder | working travel, speed and acceleration limits, defaults, jog timeout |
| Who may change it | only over USB, inside the physical presence window | any connected host: app, script, agent, network API |
| Set with | [`HardConfigSet`](/docs/api#HardConfigSet) | [`SoftConfigSet`](/docs/api#SoftConfigSet) |
| Read with | [`HardConfigGet`](/docs/api#HardConfigGet) | [`SoftConfigGet`](/docs/api#SoftConfigGet) |

A command outside the soft limits is refused with `ERROR_LIMIT_EXCEEDED`. The response carries a [`LimitViolation`](/docs/api#LimitViolation): which limit (`LIMIT_TRAVEL_MIN`, `LIMIT_TRAVEL_MAX`, `LIMIT_VELOCITY`, `LIMIT_ACCELERATION`, `LIMIT_JOG_TIMEOUT`), the requested value and the nearest allowed value, so a host or an agent can correct the request without guessing.

### Changing HardConfig: USB and physical presence

`HardConfigSet` is accepted only when both conditions hold:

1. The request arrives over USB. Over the network module it is refused with `ERROR_TRANSPORT_NOT_ALLOWED`; the network API does not offer it at all.
2. The presence window is open. A short press (under 2 s) of the light-button on the device opens a 30 s window and the device sends `EVENT_PRESENCE_WINDOW_OPEN`. Without it the request is refused with `ERROR_PRESENCE_REQUIRED`.

```powershell
upan-cli -p COM5 config set --hard home_method=limit_min limit_max_absent=yes
```

Press the light-button briefly, then run the command within 30 s.

After a successful `HardConfigSet`:

- SoftConfig is clamped into the new machine limits;
- the axis is no longer homed, because the mechanics may have changed;
- the driver is reconfigured (direction, current, microstepping).

> **Warning:** HardConfig is the last line of protection for your machine. Set travel, speed and current to what the mechanics can really take. No app, agent or network client can raise these values.

### Changing SoftConfig

Any host can change SoftConfig, as long as the new values lie inside HardConfig:

```powershell
upan-cli -p COM5 config set travel_max=80mm velocity_default=5mm/s
```

```powershell
upan-cli -p COM5 config show soft
```

The device checks the new SoftConfig and refuses it with `ERROR_LIMIT_EXCEEDED` (with the allowed value) or `ERROR_INVALID_VALUE` if it does not fit.

Both `SoftConfigSet` and `HardConfigSet` are accepted only while the axis is not moving (state idle, fault or e-stop); otherwise the answer is `ERROR_BUSY`.

## HardConfig fields

| Field | Unit | Meaning |
|---|---|---|
| `axis_kind` | — | `AXIS_LINEAR` or `AXIS_ROTARY`; selects µm or m° |
| `full_steps_per_rev` | steps | full steps per motor revolution, usually 200 (up to 10 000) |
| `microsteps` | — | 1…256; with the built-in driver only powers of two |
| `travel_per_rev` | bu | travel per motor revolution: screw lead × reduction |
| `invert_direction` | — | reverse the motor direction |
| `travel_min`, `travel_max` | bu | absolute machine travel; `travel_min` must be less than `travel_max` |
| `velocity_max` | bu/s | machine speed limit |
| `acceleration_max` | bu/s² | machine acceleration limit |
| `run_current_ma` | mA | **peak** phase current, as printed on the motor nameplate (built-in driver only, up to 5 600 mA) |
| `hold_current_ma` | mA | current at standstill, not more than `run_current_ma` |
| `limit_min_active_low`, `limit_max_active_low` | — | polarity of the limit switch inputs |
| `limit_min_absent`, `limit_max_absent` | — | no switch fitted on that side; the input is ignored |
| `home_method` | — | see [Homing](#homing) |
| `home_velocity` | bu/s | homing speed, not more than `velocity_max` |
| `stallguard_threshold` | — | StallGuard sensitivity, −64…63; higher is less sensitive |
| `encoder_enabled` | — | use the encoder input |
| `encoder_counts_per_rev` | counts | encoder counts per motor revolution (quadrature, ×4) |
| `encoder_invert` | — | encoder counts in the opposite direction (A/B swapped) |
| `following_error_max` | bu | allowed difference between commanded and encoder position; more → fault |
| `driver_mode` | — | `DRIVER_INTERNAL` (built-in TMC2160) or `DRIVER_EXTERNAL_STEP_DIR` |
| `bus_node` | — | position in a module stack: 0 = UPAN Core, 1…7 = UPAN Axis |

The device validates the whole HardConfig before saving it:

- The step rate is limited to 500 000 steps/s. A `velocity_max` that would need more is refused with `LIMIT_VELOCITY` and the highest allowed value.
- Position in steps must fit the MCU counter; travel that is too long for the chosen resolution is refused with the allowed travel.
- Features the board variant does not have are refused with `ERROR_UNSUPPORTED`: `DRIVER_INTERNAL` and StallGuard homing on the Step/Dir variant (UPC-S), an encoder on hardware without the encoder capability.

Which features a board has is reported in [`Info.capabilities`](/docs/api#Capability).

## SoftConfig fields

| Field | Unit | Rule |
|---|---|---|
| `travel_min`, `travel_max` | bu | inside the HardConfig travel; `travel_min` < `travel_max` |
| `velocity_max` | bu/s | 1 … HardConfig `velocity_max` |
| `acceleration_max` | bu/s² | 1 … HardConfig `acceleration_max` |
| `velocity_default` | bu/s | 1 … `velocity_max`; used when a command gives velocity 0 |
| `acceleration_default` | bu/s² | 1 … `acceleration_max`; used when a command gives acceleration 0 |
| `jog_timeout_max_ms` | ms | upper bound for `Jog.timeout_ms`, up to 5 000 ms |
| `display_units` | — | `DISPLAY_UNITS_METRIC` or `DISPLAY_UNITS_IMPERIAL`, UI preference only |

## Homing

Absolute moves and programs need a known position. The axis becomes homed by `Home` or by `SetPosition` (declare the current position without moving). Relative moves and jogging work without homing.

| `home_method` | What `Home` does |
|---|---|
| `HOME_NONE` | nothing: `Home` is refused with `ERROR_INVALID_STATE`; use `SetPosition` |
| `HOME_LIMIT_MIN` | moves toward the min switch at `home_velocity`, backs off slowly until the switch releases; position = HardConfig `travel_min` |
| `HOME_LIMIT_MAX` | same toward the max switch; position = HardConfig `travel_max` |
| `HOME_STALLGUARD_MIN` | moves toward min until the built-in driver detects a stall; no switch needed |
| `HOME_STALLGUARD_MAX` | same toward max |

If the switch or the stall is not found within the travel, the axis goes to fault with `ERROR_HOMING_FAILED`. When homing finishes, the device sends `EVENT_HOME_DONE`.

StallGuard homing is available only with the built-in driver (UPC-D). The right `stallguard_threshold` depends on the motor and the mechanics; 0 is a reasonable start. Tuning guidance is not final yet.

```powershell
upan-cli -p COM5 home
```

```powershell
upan-cli -p COM5 set-position 0
```

The axis loses its homed state after a fault, an e-stop, or a HardConfig change.

## Limit switches

Board revision A expects **normally closed (NC)** limit switches. The inputs pass through an inverting Schmitt trigger, so a pressed switch **or a broken wire** reads as "triggered". The matching default is `limit_min_active_low = false` and `limit_max_active_low = false`.

If no switch is fitted on a side, the open input also reads as triggered, which is the safe state: the axis will not move toward that side. To run without a switch, set `limit_min_absent` or `limit_max_absent` to `true` in HardConfig. The fields say "absent" rather than "enabled" on purpose: a host that does not know them cannot switch protection off by accident.

Behaviour of the current firmware:

- A switch triggered in the direction of motion stops the axis immediately, puts it in fault with `ERROR_LIMIT_SWITCH`, and clears the homed state.
- A move **toward** an active switch is refused with `ERROR_LIMIT_SWITCH`; a move **away** from it is allowed.
- `FaultClear` leaves the fault state once the cause is gone.

See [Wiring](/docs/wiring) for the switch connections and [Safety](/docs/safety) for the stop input.

## Where the configuration is stored

HardConfig and SoftConfig are stored together in the flash memory of the motion MCU, as a journal of records with a checksum. A new record is appended; the device loads the newest complete record at start-up.

- Power loss at any moment leaves the previous complete configuration in place.
- Configuration is saved only while the axis is still, because erasing flash pauses the step timing.
- If saving fails, the request is refused with `ERROR_STORAGE` and the configuration in use does not change. A configuration that only changed in RAM and vanished after a reset would be worse than a refusal.
- Reflashing the motion MCU firmware keeps the stored configuration.
- The record format is protobuf, so newer firmware can read configuration written by older firmware.

A factory reset (holding the light-button for 10 s at power-on) is planned to reset the network settings and SoftConfig, but not HardConfig. The procedure is not final yet.
