# NI-DAQmx

Stream and drive NI-DAQmx inputs and outputs

- Category: Data acquisition
- Capabilities: Streams, Assets, Commands, Operations, Entities
- Runs on: Linux x64, Windows x64

Read analog inputs, counters and digital lines, and drive analog and digital
outputs, on National Instruments DAQ hardware. It uses the NI-DAQmx driver
already installed on the machine the board is plugged into.

## What you get

- **Analog inputs** — the channels you tick, at the rate you set, on paths
  like `/nidaqmx/Dev1/ai0`.
- **Sensors, not just volts** — thermocouples, RTDs, thermistors, 4-20 mA
  loops, resistance and strain gauges. The board converts them, so readings
  arrive in degrees, amps or ohms.
- **Accelerometers, microphones, load cells and 4-20 mA out** — IEPE inputs,
  bridge inputs in mV/V, current output for a valve, and analog triggering on
  a level or a band. **These have never been run against an instrument.**
  Read *IEPE, bridge and 4-20 mA out — what has been proven* below before
  relying on one.
- **Your own unit in the board** — `ai0=linear:12.5,3.0 -> psi` makes
  NI-DAQmx convert the channel and report `psi` to every NI tool on the
  machine. Usually **Calibration** is the better choice; see **Turning a
  reading into engineering units**.
- **Outputs** — analog and digital, set from **Trellis** via
  `ch.write(path, value)`. Every write is recorded.
- **Whole digital ports** — `port0 = 0xA5A5` sets sixteen lines in **one**
  clock tick, on any port. A single line is still `true`/`false`.
- **Patterns, ramps and pulse trains timed by the board** — the board steps
  through digital states, ramps an analog output (a smooth line, not a
  staircase), or puts a square wave on a counter pin for a valve, stepper or
  motor.
- **One range per output** — `ao0` can be ±10 V while `ao1` is 0–5 V.
- **Counters** — frequency, period, pulse width, edge counts and quadrature
  encoders, on the same clock as the analog inputs.
- **Digital inputs** — a whole port as one number, or lines as 0/1: clocked
  with the analog inputs, polled, or read the moment a line changes.
- **Captures around an event** — external sample clock, start trigger,
  reference trigger (keeps data from *before* the event), pause trigger, and
  finite captures of exactly N samples.
- **A hardware dead-man** — if Phloem stops answering, the board drives the
  digital outputs you name to the states you name. See **The hardware
  dead-man**.
- **Self-test and reset** — see **Is it alive, and making it behave**. Reset is
  the most destructive thing this connector can do.
- **One board per input**, named by NI's **Device** name (`Dev1`). A second
  board is a second input.
- **The whole board in one table** — analog inputs, counters, ports and lines
  are rows, each with the unit it will arrive in.
- **What the board is** — model, connection, serial, channel counts, input
  ranges, what its inputs can measure and how they couple, what its outputs
  can drive. All read from the board, and used to check every setting.

## Setup

1. Install the NI-DAQmx runtime and connect the board. Phloem detects an NI
   USB DAQ and offers a draft (device name `Dev1` by default).
2. Open the connector. **Everything this machine has is already on the page**:
   a card per chassis or board, a row per filled slot, and its channels.
   - Each cDAQ module shows as you would describe it:
     `slot 2  NI 9203  current  ±20 mA  8 AI`.
   - The slot is the one the **chassis itself reports** now, through NI System
     Configuration. It is never the digit in the module's name. NI names a
     module `<chassis>Mod<N>` from the slot it was first seen in, and keeps
     that name if the card moves. So `…Mod5` in slot 3 shows `slot 3`, with a
     note on how to update NI-DAQmx's record (Reserve Chassis or Self-Test on
     the chassis in NI MAX). Channel paths do not change.
   - A module NI MAX knows but the chassis does not see keeps its old slot,
     with a note.
   - A module the chassis sees but NI-DAQmx has **no device for yet** shows in
     its slot with a note: it cannot stream until NI MAX re-detects the
     chassis.
   - Without NI System Configuration (no NI MAX, or Linux), the slot is
     NI-DAQmx's record, and the page says so. If neither knows, it shows
     `slot —`.
   - Slots start closed. A slot with a ticked channel opens itself.
     **Default board** picks the board for an unqualified channel.
3. Set the rate **on the hardware row**: a level (Slow, Medium, Fast) on each
   module's slot line. See *Rate levels*. The page shows the allowed range, and
   the measured rate once running.
4. Click a channel to set it up. Every analog input is listed, one line each:
   channel, name, **live value**, and setup. An unset channel reads *Not
   configured*; three or more in a row fold into one line.

   The **channel dialog** asks what the channel **Measures** (Force), then
   which **Sensor** reads it (a bridge). It offers only what that module can
   do, so a 9203 offers current only. Your choice fills a typical setup you
   can see and change: a bridge at quarter / 2.5 V with 2 mV/V, a 4-20 mA loop
   as two points, a thermocouple as type K with the module's cold junction.
   - **General** — name, unit, signal range, the live reading through the
     calibration you are typing, and **Same setup on** to copy it to nearby
     channels.
   - **Calibration** — Linear, Sensitivity, Two point or Table, an offset with
     **Zero** (the settled reading becomes the offset), and a label.
     **More…** opens the full editor for a polynomial, a software
     thermocouple curve or a formula.
   - **Electrical** — the hardware range, this sensor's settings, and a
     bridge's **Null** and **Shunt** calibration.

   **Apply** puts the settings into the input (saved with **Save**) and writes
   the calibration at once as a new record; the old one stays in History. On a
   new input, the calibration is written when you add the input. **Cancel**
   leaves nothing behind. **Clear channel** stops acquiring it.
5. Tick counters and digital lines on their own rows. Every channel setting
   is also written out in the `Sensor / wiring` field below.
6. Press **Test connection**. It names the board and reads every ticked channel
   as configured. Save, and the channels appear on **Live**.

### Rate levels

Each chassis module runs at a **level** — Slow, Medium or Fast — as in
FlexLogger. **Rate levels** sets the hertz behind each: 1 Hz, 100 Hz and 1 kHz
unless you change them. A module starts at the level its kind suits (a
temperature card Slow, a bridge card Fast). Changing a level's hertz moves
every module on it.

**A level is one clock.** The three levels are the three analog-input timing
engines of a Gen II chassis, so levels cannot ask for a fourth rate.

**A module that cannot run at its level's hertz shows its real rate beside the
level** — `Medium · runs at 1.65 kS/s, its floor`:

- **A delta-sigma card below its floor** (a 9234 or 9237 will not sample below
  about 1.6 kS/s) runs AT its floor. So does every module on that level,
  because the level is one clock. FlexLogger does the same. The others read
  `Fast · runs at 1.61 kS/s, Mod8's floor`. Every reading is real; nothing is
  repeated. This does not use an extra rate.
- **A slow card above its ceiling** (a 9210 or 9219 on a faster level) runs in
  **a task of its own at its ceiling** — `Fast · runs at 14.29 S/s, its
  ceiling`. This avoids repeated readings, but it uses an engine and counts as
  a rate.
- All delta-sigma modules in an input share **one** task. A 9234 on Medium
  beside a 9237 on Fast runs in Fast's task: `Medium · runs at 1.65 kS/s,
  Mod8's floor`.

**Inputs saved with a rate in hertz** (from older versions) keep running at the
same rates. The connector finds levels that reproduce them and shows the
equivalent level settings, which are saved when you open the setup. If no
levels can reproduce them, the fixes offered name every module whose rate
would change, and none is applied without your click. Before any update, the
app compares per-channel rates; if any would change, the update is held and
the dialog lists each module's before → after.

In the fields: `rate_levels = slow=1; medium=100; fast=1000`, `rate_hz = fast`,
`rates = cDAQ1Mod5=slow; cDAQ1Mod8=fast`.

A module with no ticked channels keeps its level in `rates`
(`cDAQSpaceMod7=medium`), as FlexLogger does, so it comes back at that level
when you tick a channel again. While unticked it is ignored.

### If a module cannot run at the rate you set

Modules sample differently. A scanned card (9221, 9203) takes almost any rate.
A delta-sigma card (9237, 9234) will not go below about 1.6 kS/s. A slow card
(9210, 9219) will not go above a few tens of samples per second. The connector
opens **one task per level in use**, plus one for a slow card pushed past its
ceiling. All are clocked from the chassis backplane, so nothing drifts, and
**no reading is duplicated**.

The slot row shows each module's real rate. That is information, not a
warning. A level on a board that is not in a chassis is refused; a standalone
board runs at the input's rate.

A chassis has a fixed number of analog-input timing engines: three on a Gen II
chassis such as the cDAQ-9178 or cDAQ-9189, one on a cDAQ-9171/9181/9191. Each
engine runs one rate. If the modules need **more rates than the chassis has
engines**, the input is **refused** in setup, in Test connection and at the
run:

> cDAQ1 has 3 AI timing engines, one sample rate each; 4 rates asked:
> 1.61 kS/s (cDAQ1Mod8 (the least it samples)), 200 S/s (cDAQ1Mod1), 1 S/s
> (cDAQ1Mod5), 14.29 S/s (cDAQ1Mod2 (the most it samples)) — set two modules
> to the same rate.

(Slow, Medium and Fast in use, a 9237 on Fast, and a 9210 on Fast pushed to its
ceiling as the fourth.)

The setup shows this as one notice in the chassis card, with up to two fixes
as buttons. Each fix moves ONE module onto a level another module already
uses, and is offered only if no other module's rate changes. Save is held until
it is resolved ("Resolve the conflict above"). Only modules with ticked
channels count.

Slower rates are never merged down to fit. A second input does not help: the
engines belong to the chassis.

#### Compared with FlexLogger

| | FlexLogger | this connector |
|---|---|---|
| AI rate per chassis | 3 levels (Slow/Medium/Fast), one value each | same — `rate_levels`, per input |
| assigned to | the module's AI subsystem | the module (`rates`), set on its slot row |
| delta-sigma below its floor | runs at the floor; the whole level at the "lowest common rate" | same |
| shown | the module's "actual rate" beside its level | `Medium · runs at 1.65 kS/s, its floor` |
| slow card above its level | not documented | a task of its own at its ceiling, counted as a rate |
| a module's rate in hertz | not possible | refused, converted to levels when it can be |
| delta-sigma modules on two levels | allowed | one task, at the higher need |
| Counter / Digital levels | own levels | none: counters and clocked lines use the main AI task's clock |
| downsampling (100 Hz from a delta-sigma card) | software, by default | none: the card's own rate, recorded as is, shown on the row |
| unticked module's level | kept | kept, ignored |
| channels a new input records | the ones enabled | the ones ticked; nothing ticked is refused at Start |
| a module in use elsewhere | `In use by {0}` | `cDAQSpaceMod2 is in use by another program — FlexLogger or a script; stop it there` |
| an open thermocouple's reading | logged as read (−176 427 °C) | stored as read; the row says `Open thermocouple?` |
| channel names | the physical channel, `cDAQSpaceMod8/ai0` | a new input: every board under the Label, `engineA/cDAQSpaceMod1/ai0`; an existing input keeps its paths |

Notes on the differences:

- **A slow card past its ceiling gets its own task.** Sharing a task with
  faster modules would repeat its readings.
- **Delta-sigma modules share one task.** Two would need a shared sync pulse
  the connector does not route yet.
- **No Counter or Digital levels.** Counters and clocked lines follow the main
  AI task's clock, so they share one timeline.
- **No software downsampling.** Phloem records what the card converted, at the
  rate it converted it.
- **Channel names:** an existing input keeps its paths, because calibration,
  layouts and history are keyed by them. Moving to the new naming is a rename
  you choose.
- **The simulated 9210 runs about 1 % fast**, in FlexLogger too. That is the
  simulator's clock; a real chassis shares one timebase.
- **After a restart**, Phloem keeps one timeline per channel. The stop shows
  as a gap, and any overlap with the previous run's end is marked.

A TDMS export of these channels carries the properties FlexLogger's log
carries: root and group properties (a channel-less `Test Information` group,
then `Log`), channel statistics (`minimum`, `maximum`,
`NI_ChannelValueAverage`, `novaluekey`, `monotony`), sensor scale, device and
chassis ids, terminal configuration, and the range and channel type in the
unit the channel is logged in. The differences:

| Property | FlexLogger | Phloem |
|---|---|---|
| root `Date Created` | log start | when the export was written (`datetime` is the window start) |
| `RunNumber` | 1 | not written |
| `unit_string` | `°C` as one CP-1252 byte | `°C` in UTF-8 |
| `wf_samples` | the first chunk's count | the channel's total |
| `monotony` | `not monotone` | `not monotone` only when readings rose and fell; else none |
| polynomial scale | — | no `NI_SensorScale` and no range |
| `NI_TerminalConfiguration` | `Differential` | the chosen wiring, or the only one offered; none if left at default with several |

#### Settings checked like FlexLogger

- **Setup and Test connection check the same tasks the run uses.** A setting
  the driver refuses is shown in the form's words, such as `SimBr9237 sets one
  excitation for all its channels — ai0 5 V; ai1 2.5 V — choose one for every
  channel`. The same goes for a filter that differs between a module's channels
  or is too slow for the rate. A module in use by a running input is not
  checked.
- **Excitation is a list from the module.** For internal excitation you pick
  from the module's own values (9237: 2.5/3.3/5/10 V; 9234: 0/2 mA; 9219:
  0/2.5/5 V and 0/0.5 mA). A value off the list is refused (`ai0: SimBr9237
  drives 2.5 V, 3.3 V, 5 V or 10 V — 4 V is not one of them`). An unset IEPE
  current is the module's own (2 mA on a 9234). The row and the export show
  the excitation the board will actually give. External supplies take a
  number.
- **Slow inputs can be tested.** The check waits two sample periods, at least
  5 s. Past its time budget it says what it is still reading (`Still reading
  ai0 on SimUn9219, one sample every 20 s (8 s so far)`), and the next check
  gets the answer. The rate field takes hertz only.
- **A standalone board past its limit** is shown while you edit, with a fix:
  `SimUn9219 samples at most 100 S/s — 1 kS/s is outside it`, with `Sample at
  100 S/s`. It is never changed silently.
- **Filter Auto** — `adc=auto` picks the highest-resolution filter valid at
  your rate (High resolution, Best 50 Hz, Best 60 Hz, High speed), for every
  channel of the module. The row shows which it chose.
- **Temperature units, custom RTDs, types A and C.** `units=degC|degF|K` on a
  thermocouple, RTD or thermistor sets the readings, range and unit (type K in
  °F: −328 to 2501.6). `type=custom,a=,b=,c=` on an RTD sets custom
  Callendar–Van Dusen coefficients. Thermocouple types A (100–2480 °C) and C
  (0–2315 °C) are supported. A constant cold junction is still written in °C.
  The cold junction can be the module's sensor, a constant, or another
  channel.

#### How tasks start

Counters and clocked digital lines follow the **main** task's clock (the task
at the rate you typed). On a chassis, the connector reserves that task first,
learns which timing engine it got, arms the other tasks on it, and starts the
main task last, as NI documents. An external `clock`, reference trigger or
pause trigger cannot be shared across several rates, so an input that needs
several rates and sets one is refused.

On a chassis that is not time-aware (cDAQ-9171/9174/9178), a delta-sigma module
runs on its own oscillator and can drift slightly over a long run; the
connector says so when it happens. A cDAQ-9185/9189 supplies that clock itself.
Delta-sigma modules run together at the highest rate any of them needs: a 9234
and a 9237 set to 1 kHz and 100 Hz both run at 1.65 kS/s, and each row says
why.

A finite capture is a duration: `n` samples at the rate in the box. A slower
task takes proportionally fewer samples, so every task covers the same span.

## What is wired to each input

An unticked channel is not configured, so it shows no unit and no range.

A ticked channel with nothing stated is read as **what its module measures**:
a current card in amps, a thermocouple card in °C, a voltage front end in
volts. The setup table marks it *implied*. Saving from the setup writes the
measurement into this field, so later versions read it the same way.

The choices offered are what the module **physically** measures, from the
ranges it reports. A module with no current range is not offered current; one
with no bridge range is not offered strain; a thermocouple needs a
millivolt-scale range. (NI-DAQmx also lists current, RTD and strain for a ±60 V
NI 9221 and a ±80 mV NI 9210, which need extra hardware. Phloem offers only
what the module does on its own.)

Choosing a bridge or strain measurement fills in the common wiring, a quarter
bridge at 2.5 V excitation, where you can see and change it.

State something only when the board cannot know it: a channel wired as
something other than its module's default, the thermocouple type, how an input
is referenced, what is on a bridge. The channel dialog is the short way. Its
**Sensor** offers only what the module can do. Its **Electrical** column shows
that sensor's settings and holds **Apply** until required ones are filled.
Wiring offers only what the channel's front end reports; coupling only what the
module can do. **Apply** writes the channel's whole entry into this field as
`key=value` pairs.

The field is a list separated by semicolons. An entry with no channel name
applies to every channel; an entry starting `ai3=` applies to that channel
only.

```
voltage,term=diff
ai0=thermocouple,type=K,cjc=builtin
ai1=current,shunt=internal
ai2=rtd,wires=4,excit=0.001
```

**How the input is referenced** — `term=` — matters most, for voltage and
current inputs:

| `term=` | what it means |
|---|---|
| `default` | let the board choose (what happens if you say nothing) |
| `diff` | differential — two wires, neither tied to ground. The safe choice for anything floating: a battery, a thermocouple, a sensor with its own supply |
| `rse` | single-ended, measured against the board's own ground |
| `nrse` | single-ended, measured against the board's sense terminal |
| `pseudodiff` | differential with a small path to ground, on boards that offer it |

A floating source read single-ended drifts until the reading pins to the top or
bottom of the range. It is the most common reason a correctly wired sensor
reads nonsense.

**The sensors, and what each needs.** Anything about *your bench* rather than
the board must be stated. No default could be right.

| Sensor | Write | Must state | Defaults |
|---|---|---|---|
| `voltage` | `voltage,term=diff` | — | the **Input range** select; empty = the module's own widest range (NI 9221 ±60 V, USB-6421 ±10 V) |
| `current` | `current,shunt=internal` | — | ±20 mA; `shunt=default\|internal\|external`, `shunt_ohms=249` |
| `thermocouple` | `thermocouple,type=K,cjc=builtin` | `cjc_val=` if `cjc=const`; `cjc_chan=` if `cjc=chan` | type K, cold junction from the terminal block, the type's own span (K −200 … 1372 °C, J −210 … 1200 °C, T −200 … 400 °C) |
| `rtd` | `rtd,wires=4,excit=0.001` | `wires=`, `excit=` (amps) | Pt3851, 100 Ω at 0 °C, −200–660 °C |
| `thermistor` | `thermistor,excit=0.0001,a=…,b=…,c=…` | the excitation, and `a`/`b`/`c` from the datasheet | 4-wire, 0–100 °C |
| `resistance` | `resistance,wires=4,excit=0.001` | `wires=`, `excit=` (amps) | 0–1000 Ω |
| `strain` | `strain,bridge=quarter1,excit_v=2.5` | `bridge=`, `excit_v=` (volts) | gauge factor 2.0, 350 Ω, Poisson 0.30, ±1000 µε |
| `builtin_temp` | `builtin_temp` | — | the board's own temperature, 0–100 °C |
| `accel` ¹ | `accel,sens=100` | `sens=` (mV/g, off the sensor) | ±5 g, internal 4 mA excitation, AC coupled, `sens_units=mv/g`, `units=g` |
| `microphone` ¹ | `microphone,sens=50` | `sens=` (mV/Pa, off the microphone) | 100 dB SPL, internal 4 mA excitation, AC coupled |
| `bridge` ¹ | `bridge,bridge=full,excit_v=2.5` | `bridge=` (`full`/`half`/`quarter`), `excit_v=` (volts) | 350 Ω, the module's own bridge range (a 9237's ±25 mV/V; ±2 mV/V on a module that states none), `units=mv/v`; `null_v=`, `gain_adjust=` written by the calibration buttons |
| `freq_voltage` ¹ | `freq_voltage,threshold=1.5,hysteresis=0.2` | `threshold=` (volts), `hysteresis=` (volts) | 1 Hz – 100 kHz |
| `lvdt` ¹ | `lvdt,sens=50,excit_v=1` | `sens=` (`sens_units=mv/v/mm\|mv/v/mil`), `excit_v=` (volts) | ±0.1 m, `units=m\|in` (a non-default unit needs `min=`/`max=`), 2.5 kHz excitation, `wires=4\|5` |
| `rvdt` ¹ | `rvdt,sens=2,excit_v=1` | `sens=` (`sens_units=mv/v/deg\|mv/v/rad`), `excit_v=` (volts) | ±70°, `units=deg\|rad`, 2.5 kHz excitation, `wires=4\|5` |
| `force_iepe` | `force_iepe,sens=10` | `sens=` (`sens_units=mv/n\|mv/lb`) | range = the module's volts ÷ sensitivity (10 mV/N on ±5 V is ±500 N), `units=n\|lb\|kgf`, internal 4 mA excitation, AC coupled |
| `velocity_iepe` | `velocity_iepe,sens=100` | `sens=` (`sens_units=mv/mm/s\|mv/in/s`, off the sensor) | range = the module's volts ÷ sensitivity, `units=m/s\|in/s`, internal 4 mA excitation, AC coupled |

¹ **Never run on real hardware.** See *[What has been proven, and
what has not](#iepe-bridge-and-4-20-ma-out--what-has-been-proven)* below before
you rely on one.

**Range.** Add `min=` and `max=` in the sensor's own unit —
`thermocouple,type=K,min=-200,max=1372`. The board picks the closest setting
that contains it, so a wider range costs resolution and nothing else.

**Thermistor range** is different. The driver works out what your `a`/`b`/`c`
and excitation can represent, and refuses anything outside, naming the bound.
The 0–100 °C default suits every ordinary NTC. If it refuses a colder range,
that number is the real limit of your part.

**Frequency from an analog input.** `freq_voltage` counts how often a signal
crosses a voltage and reports **hertz**. Use it for a tacho coil, flow turbine
or optical sensor whose output is not a clean logic edge. `threshold=` is the
crossing level; `hysteresis=` is how far the signal must fall back before the
next crossing counts. State both: zero hysteresis counts noise and reports a
frequency several times too high. Its `min=`/`max=` are in **hertz**, not
volts.

**Accelerometers and microphones (IEPE).** The board sends a small current up
the sensor cable and reads the signal back on the same wires, with AC coupling
removing the DC bias. So the board must support both. An NI-9234 is the usual
choice. If the board cannot, you are told which board and what it supports,
before anything is recorded.

You must enter the **sensitivity** printed on the sensor: "100 mV/g" is
`sens=100`; a microphone's "50 mV/Pa" works the same. A wrong value gives
readings off by a factor, in the right unit, so there is no default. Write
`sens_units=v/g` for a sensor rated in volts per g. An accelerometer reports in
`g` unless you write `units=m/s2`.

**Bridges — load cells, pressure transducers, torque cells.** A `bridge`
channel gives the bridge's output ratio in mV/V, the unit on its datasheet.
Turn that into newtons or bar with scaling (next section): a cell rated 2 mV/V
at 500 kg is `linear:250000,0 -> kg`, or whatever your calibration certificate
says. The driver's own force/pressure/torque bridge channels are not offered,
so there is one place to look when a number is wrong.

State how many arms are active (`bridge=quarter`, `half` or `full`) and the
excitation voltage, since both scale every reading. A strain channel uses a
*different* vocabulary (`bridge=quarter1`), because it also names the gauge
arrangement. Each refuses the other's words.

**Nulling and shunt calibration.** These match FlexLogger's *Null offset* and
*Shunt calibration* buttons, and the row offers both for a bridge channel.

- **Null offset** reads the unloaded bridge and stores the offset on the
  channel (`null_v=…`, in volts). It is subtracted from every later reading.
- **Shunt calibration** switches a known resistor across one arm
  (`shunt_ohms`; a 9237's own is 100 kΩ, or one you wired), measures, and
  stores a gain correction (`gain_adjust=…`).
- Both are saved in the channel's `sensor` entry, so they survive a restart.
  Delete the two words to clear them.
- **The null comes first.** A shunt taken before the null stores a wrong gain.
  So a shunt with no stored null is refused, and the button reads **Null, then
  run** and does both. Keep the bridge unloaded for both. Pass `without_null`
  if your bridge needs no null.
- A gain correction outside 0.5–1.5 (FlexLogger's window) means a wrong
  resistor value or arm, so it is refused.
- On a simulated module, nulling works (the offset comes back near zero) and
  shunt calibration is refused by the driver.

**Coupling.** `coupling=ac|dc|gnd` on a voltage, accelerometer or microphone
channel. IEPE channels are AC-coupled unless you say otherwise; everything else
stays as the board has it. If the board cannot do it, you are told what it can
do.

**Cold junction.** A thermocouple measures relative to where its wires land,
so that point must be measured too. `cjc=builtin` uses the terminal block's own
sensor and is right whenever the hardware has one. `cjc=const,cjc_val=23.5`
uses a fixed number. `cjc=chan,cjc_chan=…` reads another channel.

**An open thermocouple is stored as it read.** A broken or unplugged
thermocouple floats to values like 2400 °C or −593 578 °C. The reading is kept
exactly as the module gave it, as FlexLogger does. The setup row reads `Open
thermocouple?`, and the log says which channel, once when it starts and once
when it ends. Either check triggers it:

- **NI-DAQmx's open-thermocouple detection**, on for every thermocouple whose
  module has it.
- **The type's own range** (type K: −200 to 1372 °C). This covers modules
  without detection. NI's simulated 9210 refuses detection, so only this check
  runs there.

**The board says what it cannot do before recording.** A board that does not
measure thermocouples is refused by name, with what it does measure. A board
with no built-in excitation for an RTD or strain gauge is refused by the NI
driver, naming the setting. Add `excit_src=external` and wire your own supply.

A setting that does not apply to the chosen sensor is refused, not ignored —
for example `term=` on a thermocouple, where the board fixes the wiring.

## Turning a reading into engineering units

There are **four** ways to turn volts into psi, newtons or degrees. Use the
first one that applies.

**1. Wire it as what it is.** For a thermocouple, RTD, thermistor, 4-20 mA
transmitter or strain gauge, say so in **Sensor / wiring** (above). The board
converts it, at full rate, with a cold junction it can measure. This is the
right answer more often than people expect.

**2. Calibrate the channel.** For everything else — a load cell's slope and
offset, a pressure transducer's two-point fit, a bench correction — use the
**Calibration** column of the channel's dialog (linked from **Inputs**). It
opens on the identity line (slope 1, offset 0) in the board's unit. Press
**Zero** to tare, or enter a slope, a sensitivity (the certificate's "2 mV/V at
5 kN"), two points or a table. A software thermocouple conversion is offered
only for a channel that reads volts. Every save is recorded and versioned, and
shows raw beside calibrated. It is the only way that keeps the raw number.

**3. Let the driver scale it — `Channel scaling (at source)` with a unit.**
Write the transform and its unit:

```
ai0=linear:12.5,3.0 -> psi         # 12.5 x volts + 3.0, and the answer is psi
ai1=poly:0.0,4.2,-0.11 -> N        # 4.2 x V − 0.11 V², in newtons
ai2=linear:6250,-25 -> bar         # a 4-20 mA loop reading 0–100 bar
```

With a unit, Phloem hands the scale to NI-DAQmx as a **custom scale**. Choose
this over (2) only for these reasons:

- The **board** is set up in your unit. Any NI tool on the machine (NI MAX,
  FlexLogger) sees `ai0` as `psi`.
- The value is in engineering units **before it leaves the board**.
- The board's **input range** is chosen for the quantity, so it picks the right
  gain for 0–100 psi rather than for ±10 V.

Without `-> unit`, the connector applies the same arithmetic after the read.
That also happens on channels the driver cannot scale (thermocouple, RTD,
thermistor, the board's temperature sensor). Either way it is exactly **one**
conversion. **Test connection** shows which, per channel.

**4. Do it downstream.** A Trellis expression or a chart formula. Fine for a
one-off, but it is not recorded with the channel.

### When NOT to use the source scaling of (3)

Almost always. Compared with (2):

- **Calibration is refused on a channel scaled at source**, since there is no
  raw reading to calibrate. Phloem says so.
- **The raw volts are gone.**
- **It is not versioned.** A change is a config edit, not a recorded
  calibration.
- **It is not a sensor type.** For a K-type thermocouple, use (1). `tc_k:` is
  refused here, and the message says what to write instead.

Use (3) only when the *unit itself* must be true at the board: other NI tools
read the same card, or the signal uses a small part of the board's span. Use
**Calibration** for everything else.

### Details worth knowing

- The scale is registered with NI-DAQmx as `phloem_<device>_<leaf>`, such as
  `phloem_Dev1_ai0`, visible to other NI software. A scale you made in NI MAX
  with that exact name would be overwritten.
- **Input range** stays in volts. Phloem converts it through the scale, so
  ±10 V stays ±10 V of input.
- A polynomial must be invertible for the board to pick a range; NI fits the
  inverse over your input range. A curve that doubles back may land on a
  neighbouring gain setting.

## What the board counts

Counters measure what volts cannot: shaft speed, stage travel, pulse width,
time between a command and its confirm. Tick a counter (`ctr0`) in
**Channels** and it streams beside the analog inputs.

A counter with no **Counters** entry counts rising edges. For anything else:

| Write | Measures | Unit |
|---|---|---|
| `edge_count` | edges, cumulatively | counts |
| `freq,min=10,max=100000` | frequency | Hz |
| `period,min=1e-4,max=1` | time between like edges | s |
| `pulse_width,edge=rising` | how long one pulse was high (`falling` = low) | s |
| `two_edge_sep,first=…,second=…` | time from an edge on one wire to an edge on another | s |
| `ang_encoder,ppr=1024` | shaft position | deg (`units=rad\|ticks`) |
| `lin_encoder,dist=1e-5` | linear position | m (`units=in\|ticks`) |

- An encoder must state its scale: `ppr=` pulses per revolution, or `dist=`
  metres per pulse. A wrong value still looks like degrees.
- **`decoding=`** defaults to `x4` (every edge of both channels). An X4 encoder
  read as `x1` reports a quarter of the motion.
- `zidx=on` resets the count on the index pulse, with `zidx_val=` and
  `zidx_phase=`.
- Each counter uses its default terminal. `input=/Dev1/PFI8` points it
  elsewhere. A two-edge separation takes `first=` and `second=`; an encoder
  takes `a=`, `b=` and `z=`.
- **`semi_period` is not available.** It needs one sample per edge, which
  cannot share the analog clock. Use `period` and `pulse_width`.
- A counter's **unit** follows what it measures: `Hz`, `deg`/`rad`, `s`, or
  `counts`.

**Counters share the analog inputs' clock**, so counter sample *k* and analog
sample *k* are the same instant. An input with counters and **no** analog
channels needs a clock: set `Sample clock` → `source=/Dev1/PFI0`, or add an
analog channel. Otherwise it tells you.

## What the digital lines say

Digital inputs answer yes or no: is the limit switch closed, did the confirm
come back. Tick them in **Channels**, port first, then lines:

| Tick | You get | Path |
|---|---|---|
| `port0` | the whole port as **one number** — the integer its lines form | `/nidaqmx/Dev1/port0` |
| `port0/line3` | **that one line**, as 0 or 1 — shown as a state (on / off) | `/nidaqmx/Dev1/port0/line3` |

- A line shows as a lamp in a Number view on Live and Replay. Everywhere else
  it stays 0 and 1. Give it a `scale` and it is a plain number.
- A port stays one channel. Tick the lines you want, and each becomes its own
  channel.
- You cannot mix ports and lines in one input. The connector names both and
  refuses.

### How they are sampled

**Digital lines are sampled** — a select under *Timing*. The first choice suits
almost everyone.

| Choice | What happens | Timestamps |
|---|---|---|
| *with the analog clock when there is one, else when asked* | clocked with the analog channels if there are any, polled if not | |
| *on the shared sample clock only* | insists on the shared sample clock, and says so if there is none | the board's |
| *when asked, at the sample rate* | polls the pins at the sample rate | this computer's |
| *the moment a line changes* | samples the moment a line changes | this computer's, at the read |

- **Clocked** puts digital and analog samples at the same instant. Lines with
  no analog channels can still be clocked: set `Sample clock` →
  `source=/Dev1/PFI0`.
- **Change detection** samples on a transition, for a switch that closes once
  an hour. Name the lines with `rising=port0/line0:3` and `falling=…`, joining
  groups with `+` (`rising=port0/line0:3+port0/line7`). The default is every
  ticked line, both edges. A board without change detection is refused.
- **Polled and change-detection samples are stamped by this computer** when
  they arrive. The order of transitions is the board's; sub-millisecond timing
  is not. So these cannot share an input with analog channels or counters. Give
  the digital lines their own input, or clock them.

## When it samples, and what starts it

The *Timing* block captures around an event. Leave every control on its first
choice for the board's own clock, rising edge, running until stopped.

**Every list here comes from the board.** The pins offered are the ones you can
wire: `PFI0…PFI15` on a USB-6421, `PFI0`/`PFI1` on a cDAQ chassis, none on a
standalone USB-9237 (which then shows no clock or trigger picker). An analog
trigger is offered only if the board has both the comparison circuit and an
`APFI` input; the USB-6421 has no `APFI`, so it gets a pin-edge start only.
There is nothing to type, so nothing to misspell.

**Sample clock** — what paces the samples.

| Control | Means |
|---|---|
| **Sample clock** · *On-board timebase* | the board's own clock (the default) |
| **Sample clock** · a pin | take the clock off that wire instead |
| **Sample on** · rising / falling | which edge of that clock a sample is taken on (appears once a pin is chosen) |
| **Run** · until stopped / for a set number | continuous, or exactly **Samples per channel** then stop |

With an external clock, **Sample rate** only sizes the buffers. The wire sets
the real pace.

**Start** — what starts it.

| Choice | Then | Means |
|---|---|---|
| *Immediately, when the input starts* | | begin as soon as the task is armed (the default) |
| *When a pin edges* | **On pin**, **Edge** | wait for that edge, then begin |
| *When a signal crosses a level* | **On input**, **Level (V)**, **Edge** | an analog trigger — offered only when the board has an `APFI` input |
| *When a signal enters or leaves a band* | **On input**, **Band bottom/top (V)**, **Fire when** | a window trigger — the question an edge cannot ask: *record when the pressure leaves 95–105 psi* |

**Keep samples from before an event** — the **reference trigger**. A start
trigger begins recording at the event; a reference trigger keeps what led up to
it. It offers the same kinds as Start (none on a board without reference
triggering), plus **Samples to keep from before**, which has no default. It
needs **Run: for a set number of samples**; the rest of the count is what comes
after the event.

**Pause while a pin is** high / low — stops the clock while that line is held.

**Analog triggering has not been seen to fire.** No available board can take
one, and NI does not simulate triggering. What is checked: the settings reach
the driver correctly, and a board that cannot do it offers no choice. See
*[What has been proven, and what has not](#iepe-bridge-and-4-20-ma-out--what-has-been-proven)*.

**Put the sample clock on / Put the start trigger on** — route this board's
signals out on a pin so another instrument can follow. The reference trigger
can be exported too, once set.

## More than one module, and what one clock can serve

In a cDAQ chassis, **every module is its own DAQmx device**, with its own name,
measurement types and rate range. The chassis carries the timing engines. All
of them are listed in the channel table.

One task has ONE sample clock, and not every pair of modules can share one:

* **Rate range.** A delta-sigma module (an NI-9237 bridge card, an NI-9234
  IEPE card) has a floor of about a kilosample per second. A slow module (an
  NI-9210 thermocouple card) has a ceiling of a few tens of samples per second.
  No rate serves both.
* **How it samples.** A simultaneous module has one converter per channel; a
  scanned module shares one converter across channels.

The setup shows what runs where, per module, **before the run**. See "If a
module cannot run at the rate you set" above. Mixed modules are supported, not
refused.

## More than one board

One input can record channels from several NI devices. Whether they line up
depends on how the boards are connected. There are three cases, and the
connector tells you which one you are in.

Write channels on another board with the device in front:

```
channels = ai0, ai1, cDAQ1Mod2/ai0, Dev2/ctr0
```

Each channel still arrives on `/nidaqmx/<device>/<channel>`, so nothing
collides.

**The Label — one name for every channel.** Set **Label** to what is wired to
the board (`engineA`) and every channel is named under it:
`/nidaqmx/engineA/ai0`. Another module keeps its own name under it,
`/nidaqmx/engineA/cDAQ1Mod2/ai0`, so two modules' `ai0` stay separate. Blank
keeps the board names. The setup table shows the labelled path before you
save. Outputs keep the board name (`/nidaqmx/Dev1/ao0`). A `/` in a Label is
refused.

**Changing the Label of an input with channels renames them.** Calibration,
names, units, layout tiles, pipelines and setups move with them, after you
confirm ("Moves 8 channels, 1 calibration, 2 layout tiles; history before now
stays under the old name"). Past recordings keep the old name, and Replay shows
"previously /nidaqmx/Dev1/ai0" beside the channel. An export spanning the
rename includes both. A rename is refused, with the reason, if a new name is
taken or a setup builds the path in a way only its author can change. From a
terminal: `phloem input rename-prefix "<input>" engineA` (`--dry-run` first).

### 1. Modules in one cDAQ chassis — works, nothing to declare

Modules in one chassis share its timebase. They run in **one task**, and
sample *k* on one module is sample *k* on another, exactly. No wiring, no
settings, no "master" to choose. Design your rig around this case if you can.

### 2. Separate boards with a wire — works, and you have to say so

Two boards on two cables share nothing until you wire a signal between them,
and software cannot detect a cable. So wire it, then fill in the
**Multi-device sync** block. It appears only when your ticked channels span
two boards with no shared timebase:

* **Master board** — whose clock and start the others follow
* **Sample clock wire on pin** — master's `PFI0` → every other board's `PFI0`
* **Start trigger wire on pin** — master's `PFI1` → every other board's `PFI1`

Pins are named by their **leaf** (`PFI0`), and each board uses its own pin of
that name.

You will not see this block for boards that are already joined: modules in one
cDAQ chassis, boards in one PXI chassis, or two Ethernet cDAQ-9185/9189 chassis
that start together over the network. Boards that both list RTSI terminals may
have a ribbon cable; that is the one case the page asks about.

**Both wires are required.** Without the shared start, the boards run at the
same rate but with an unknown, hidden offset.

The master is **always declared, never guessed** from channel order.

### 3. Separate boards with no wire — refused, with the recipe

Boards that are neither in one chassis nor wired together are **refused before
anything starts**. The message names the boards and gives the ways forward.
Their clocks drift apart by hundreds of parts per million.

To record two unsynchronised boards anyway, give each its **own input**. You
get the data, but no sample alignment across boards.

### When one board dies mid-run

* **A chassis** fails as a whole: one task, one failure.
* **In a wired rig**, a follower board's failure leaves a **gap on exactly that
  board's channels**. The master and the other boards keep streaming.
* **If the master dies, the input ends**, since it is the clock.

### What has been proven, and what has not

**Proven** with two simulated boards: the real driver accepts every step of the
wired setup in order, and a read of *N* samples on the master matches *N* on
the follower.

**Not proven:** simulated boards have no cable or signal, and triggering is not
simulated. So the setup calls are tested, not the alignment. That needs two
real boards and a real wire. **The chassis case has not been run at all**; it
is covered by tests only.

## A chassis on network time (802.1AS / TSN)

A cDAQ-9185 or cDAQ-9189 can sync over the network (IEEE 802.1AS) to a
**grandmaster** clock, accurate to within a microsecond. Phloem then trusts the
chassis's timestamps as they are, and records the input's clock as
`disciplined`, its most trusted alignment.

This happens only when **all** of these hold. Otherwise the run is timed
against this computer's clock as usual.

* **Every board in the input is a module of a cDAQ-9185/9189** (or the chassis
  itself), and **NI-Sync reports each chassis synced to one grandmaster that
  is not the chassis itself**. A chassis alone on a network becomes its own
  grandmaster and free-runs, so it does not count.
* **The chassis's own sample clock paces the input**, not an external `clock`.
* **Timestamps come from the chassis.** If the chassis time is more than a
  second away from this computer's clock, the run is timed by this computer
  instead.

The status is checked every 2 s. It is **withdrawn** the moment a chassis loses
sync, reports another grandmaster, or stops answering, and when the input ends.
It is **not** restored for the rest of that run. Restart the input to trust
the clock again.

**NI-Sync is a separate NI driver**, needed on this computer. Without it,
nothing is trusted. The stated accuracy (1 µs) comes from the cDAQ-9185 and
cDAQ-9189 datasheets ("Network synchronization accuracy <1 µs"). Models not on
that list are never trusted.

**Per input.** Each input's status covers only its own readings. Run each
chassis as its own input. One input spanning two chassis is refused unless
they share a timebase.

### What was verified, and what was not

**Verified:** on a test machine with only simulated devices (NI-DAQmx 26.5),
nothing reports network time, so the connector trusts nothing and times runs
as usual.

**Not verified:** NI cannot simulate a 9185/9189, and the test machine has no
grandmaster. Untested: opening a real cDAQ-9189 through NI-Sync; whether the
chassis clock is UTC or TAI (37 s ahead; if TAI, the 1 s check rejects it and
the log shows about 37 000 ms); and long-run phase lock. Any failure falls
back to untrusted, never a false claim.

**To test it for real:** two cDAQ-9185/9189 chassis and an 802.1AS
grandmaster (a TSN switch, a third NI TSN device, or NI-Sync on a PXI timing
module), with NI-Sync on the host and chassis firmware ≥ 18.6. Run one input
per chassis, feed one signal (say a 10 Hz square wave through a BNC tee) to
both, record a minute, and check the edges line up within ±1 µs plus one sample
period. Unplug the grandmaster: both inputs should log the loss within 2 s.

## Each task is its own clock

An input whose modules run at different rates uses one task per rate. Each
task's timestamps are anchored to this computer's clock separately, so each
carries its own small error. On a simulated chassis, the 9210's task ran about
1 % fast while the others tracked the host.

So Phloem models each task's clock on its own, per run. A counter or clocked
digital line shares the main task's clock. A TSN chassis keeps its trusted
network time (above) across all its tasks.

## The hardware dead-man

Phloem's other safety features (interlocks, STOP, driving channels to safe
values) need Phloem to be **running**. If it is killed, runs out of memory, or
the computer hangs, the board's outputs hold their last value. A valve stays
where it was.

The dead-man moves that last step into the **board**. You give the card a
timeout and a state for each digital output. If nothing pets it in time, **the
card itself** drives those lines there.

Set **Dead-man timeout (s)** in the *Outputs* block, and a safe state on each
digital-output row. Leave the timeout empty for no dead-man.

- The **timeout** is how long outputs may hold after petting stops, in seconds.
  **There is no default.** It depends on your process.
- One entry per guarded line: `do0=low`, `do1=high`, `do2=tristate`, spelled
  as you write the line to command it. `tristate` is for a line whose safe
  state comes from a pull resistor on the board it drives.
- `no_change` is refused: it would do nothing.
- Not every NI board has the timer (it is a cDAQ / cRIO / X-series feature).
  If the board lacks it, the refusal names the board and quotes its answer.

### Arming it, and who pets it

Nothing is armed automatically. Four operations:

| operation | what it does |
|---|---|
| `deadman.arm` | Programs the card and starts the timer |
| `deadman.pet` | Resets it — call this at least as often as the timeout |
| `deadman.clear` | Acknowledges an expiry so the lines can be driven again |
| `deadman.disarm` | Stops the timer; the outputs are left where they are |

**Petting is not automatic.** Whatever pets it should be something that would
**stop if the computer failed**: the safety loop, or the test itself. Then any
failure along the way — Phloem killed, the computer hung, the connector crashed
or shut down — stops the pets, and the card drives the outputs.

So **arming is a commitment**: with nothing petting, the stand goes safe one
timeout later. Saving the form never arms it.

- Writing a setpoint does not pet it. Operations run one at a time, so a pet
  can wait behind a write or a board rescan. Choose a timeout well above your
  pet interval plus the slowest operation. Below 10 ms is refused; that needs a
  PLC or FPGA.
- An expiry **latches**. Until `deadman.clear`, writes to the guarded lines are
  refused, so a person must re-enable the stand.

### What the safe state is, and where it should come from

Today you state it twice: in the test (`ch.write(path, safe=…)`, used by STOP
and interlocks) and in this field. The connector cannot read the test's value.
**Keep the two in agreement.** If they differ, the dead-man's value is the one
that runs when nothing else can.

### What an armed dead-man does and does not block

It guards **digital output lines** only:

| While a dead-man is armed | |
|---|---|
| Writing a digital line or port | **always allowed** — that is what it is for |
| A hardware-timed digital pattern | allowed, unless it drives a line the dead-man guards |
| An analog ramp | refused — the dead-man cannot reach an analog output |
| A pulse train | refused — nor a counter |

The refusals stop you believing the board will make something safe that it
will not. See **The dead-man and a digital pattern** below.

### What has been proven, and what has not

On a *simulated* board, the dead-man configures, arms, reports its timeout,
pets, clears and releases, and armed lines still accept setpoints, including a
whole-port write.

**An actual expiry has never been seen.** NI's simulated boards never expire
(armed at 100 ms, starved for a second, still not expired). So two claims rest
on NI's design, not a test: that an expiry drives the lines, and that the
dead-man outlives Phloem being killed. Verify both on your real board before
trusting the stand to them.

## What each analog output may drive

**Analog output range** — a select on each analog-output row, from the ranges
the board reports. Left at default, an output uses the **Input range**. Written
out, it is `<channel>=<min>:<max>` in volts:

```
ao0=-10:10; ao1=0:5
```

A bare `0:5` sets the default for every output; a named entry overrides one.
An unknown channel, or anything that is not a pair of numbers, is refused.

**4-20 mA out.** Put `current:` before the range to drive a current loop, the
usual way to command a valve position:

```
ao0=0:5; ao1=current:0.004:0.02
```

Values are in **amps**, so 4-20 mA is `0.004:0.02`, and every value you command
on that channel is in amps. `voltage:` is allowed for clarity; no type word
means volts. A board that cannot drive current is refused, with what it can
drive. **This has never been run on real hardware** — see *[What has been
proven, and what has not](#iepe-bridge-and-4-20-ma-out--what-has-been-proven)*.

The range does two things:

- **A setpoint outside it is refused** before the board is touched. Capping
  `ao1` at `0:5` means a stray `-3` never reaches the valve.
- **The board is set to it.** DAQmx picks the smallest hardware range that
  fits, so a 0–5 V output gets twice the resolution of one at ±10 V where the
  board has a 0–5 V range.

If this field has an error, **no analog output is written at all** until it is
fixed, and the connector says why. It never falls back to ±10 V.

## Ramps the board clocks out

A ramp built from setpoints sent by this computer is a staircase, and the
recording shows it. Instead, Phloem can give the board the whole ramp and let
its own clock play it, smooth and evenly spaced. On an NI USB-6421, a
1000-sample ramp over 1 second was 245 samples in at 200 ms and complete at
1 s.

**How you ask for one.** Four operations, all local to this device. The
assistant and the cloud cannot call them.

| operation | what it does |
|---|---|
| `command.ramp` `{path, to, over, from?, rate_hz?}` | Take that output to `to` volts over `over` seconds. |
| `command.waveform` `{path, samples, rate_hz}` | Play exactly these numbers, one per clock tick. |
| …with `path` as a **list** | Several outputs on one clock — see below. |
| `command.stop` `{to?}` | End it now. Reports the voltage the output was left at, or drives `to` if you name one. |
| `command.status` | Is one running, how far in, how long left. |

`rate_hz` is samples per second clocked out. It defaults to 1000 and is checked
against the board's maximum output rate.

**Several outputs, moving together.** Give `path` a list, and `to` (or
`samples`, or `from`) a matching list in the same order:

```
command.ramp {
  path: ["/nidaqmx/Dev1/ao0", "/nidaqmx/Dev1/ao1"],
  to:   [4.0, 1.5],
  over: 2.0
}
```

They run as **one task on one clock**, starting together. Two separate
commands would start at slightly different, unknown times.

- `over` and `rate_hz` are shared.
- Channels of different lengths are refused, never padded.
- All channels must be on the **same board**.
- Each keeps its own range, and `command.status` and `command.stop` report
  every channel.

**The starting point must be known.** An analog output cannot be read back. A
ramp starts from the last value Phloem commanded on that channel this session;
otherwise it asks for `from`, rather than jumping from 0 V.

### What a running ramp will and will not do — read this before using it

Once loaded, **the board plays the ramp whether or not Phloem is alive.** An
interlock, a killed process or a change of mind does not reach the board. So:

- **It always ends.** A ramp is a fixed number of samples, never repeated.
  There is no continuous option.
- **Nothing longer than 60 seconds.** A longer ramp is refused, and the message
  points at the alternative: drive the output from the host with ordinary
  setpoints, stepped but stoppable at any time. (60 s is a chosen limit, not a
  hardware one.)
- **When it ends, the output stays at the final value.** A chamber taken to
  400 psi stays at 400 psi. Phloem then writes that value as an ordinary
  setpoint, so it is recorded. For a safe value afterwards, use a **safe value**
  on the write channel, or the hardware dead-man.
- **Stopping tells you where it stopped.** `command.stop` reads how far the
  board got, drives that voltage and reports it. `command.stop {to: 0}` drives
  a value you name instead.
- **It cannot run while the hardware dead-man is armed, and vice versa.** The
  dead-man guards digital lines only, so it would not stop the ramp. Disarm the
  dead-man to ramp, or stop the ramp to arm it.
- **One at a time.** A second ramp is refused while one runs.
- **If Phloem is killed outright**, the driver stops the ramp. The 60 s limit
  covers the cases where it does not: the computer wedged but alive, the
  machine hung with the driver still loaded, or a stop that cannot arrive in
  time. Then the whole guarantee is: *it ends within a minute*.

### What has been proven, and what has not

**Proven** on the real driver: the board clocks the ramp at the asked rate and
finishes on its own; two outputs in one task hold different ranges (`-10/10`
and `0/5`); a ramp can be stopped part way and reports its progress; a
setpoint to a generating channel is refused; a too-long ramp is refused before
the board is touched.

**Not proven:** that any voltage appeared on a pin. The board is simulated and
cannot read its outputs back. The buffer contents and settings are covered by
tests only. Also untested: a board with several output ranges picking a
different range per channel (the test board has only ±10 V).

## Pulse trains and PWM

A board's **counters** can put a square wave on a pin to drive a proportional
valve, set a motor speed, step a stepper, open a shutter, or give other
instruments a clock. You set a **frequency** and a **duty cycle** (the fraction
of each cycle the pin is on); the board does the rest.

There are **three commands**:

- **`command.pwm`** — hold it. *"Drive the valve at 30 %."* Runs until you stop
  it. **No time limit.**
- **`command.pulses`** — count them. *"Emit 240 pulses."* Exactly that many,
  then stops.
- **`command.pwm_sequence`** — step through them. *"1 kHz, then 2 kHz, then
  4 kHz, one per tick of this clock."* See below.

Each takes a counter path (`/nidaqmx/Dev1/ctr0`), the frequency in hertz, and
optionally a `terminal` for a specific pin.

```
command.pwm    { path: "/nidaqmx/Dev1/ctr0", freq_hz: 1000, duty: 0.25 }
command.pulses { path: "/nidaqmx/Dev1/ctr0", freq_hz: 200, count: 240 }
command.stop   { }
```

- `duty` is **required** on `command.pwm`, because the duty cycle is the
  command. On `command.pulses` it defaults to 50 %.
- `command.stop` and `command.status` cover pulse trains **and** ramps.

### An exact division instead of a frequency

A frequency is rounded to what the board's timebase can divide to: 3 kHz off
100 MHz is exact, 3.3 kHz is 3300.33 Hz. For a clock other instruments follow,
that rounding becomes drift. So you can state the division instead:

```
command.pwm { path: "/nidaqmx/Dev1/ctr0",
              ticks: { source: "/Dev1/100MHzTimebase", low: 50000, high: 50000 } }
```

The pin is low for `low` ticks and high for `high`, so the period is exactly
`low + high` ticks: 100 000 ticks of 100 MHz is 1 kHz. `ticks` replaces
`freq_hz` and `duty`; giving both is refused. The answer reports the resulting
frequency for the board's named timebases. For a terminal carrying a wire,
`freq_hz` comes back **null**. The duty cycle is always exact.

### A train per tick — stepping through setpoints

`command.pwm_sequence` gives the board a whole profile and a clock to step it,
for a stepper's acceleration, a fan curve or a sweep:

```
command.pwm_sequence {
  path:  "/nidaqmx/Dev1/ctr0",
  clock: "/Dev1/ai/SampleClock",
  rate_hz: 100,
  steps: [ {freq_hz: 1000, duty: 0.5},
           {freq_hz: 2000, duty: 0.5},
           {freq_hz: 4000, duty: 0.5} ]
}
```

Each tick of `clock` moves to the next step. `clock` is **required**: a counter
has no clock of its own. Use the recording's sample clock (as above), another
counter's output, or a wire.

### 0 % and 100 % are not pulse trains

`duty` must be strictly between 0 and 1. **Fully closed** is no train: stop it,
and the pin rests at its idle state. **Fully open** is a level: hold a digital
line with `ch.write`, or wire the valve to a counter whose idle state is on and
leave it stopped. Phloem refuses both ends and says where to go instead.

### Where the pin rests when the train stops

**The counter's idle state**, set once on the counter's output row (**Rests
low** / **Rests high**; written out, `ctr0=low; ctr1=high`). The default is
`low`, the de-energised state. Write `ctr0=high` if your stand is wired the
other way.

That level applies to **everything that ends a train**: a stop, a finite train
finishing, the tool session ending, and this computer being killed. It is part
of how the channel is created, so it does not depend on Phloem being alive. A
command cannot override it. Every answer repeats the idle state in use.

### Why there is no time limit, when a ramp has one

A ramp is a movement that should finish; a pulse train like *"hold this valve
at 30 %"* has no natural end. A ramp also has a slower, stoppable fallback
(host setpoints). A 1 kHz square wave has no such fallback, so a time limit
would remove the feature. Instead, a train stops when you stop it or when the
tool session ends, and `command.pulses` is always finite.

### The dead-man does not cover a counter, so the two are refused together

The dead-man drives **digital output lines**, and a counter output is not one.
So while a dead-man is armed, a pulse train is **refused**, and while a train
runs, arming a dead-man is **refused**. Stop the train or disarm the dead-man.
This should lift once a board supports counter expiration states; the test
board does not.

### One counter, one job

`/nidaqmx/Dev1/ctr0` is the same path a counter *reading* uses, because it is
one physical counter: either counting or generating. Asking for both is
refused (*"the specified resource is reserved"*). A dedicated frequency-output
pin, where a board has one, is `/nidaqmx/Dev1/freqout`.

### What has been proven, and what has not

**Proven** on the real driver: a train starts on a counter, and the driver
reports back the **frequency**, **duty cycle**, **idle state** and **output
terminal** it was given; both idle states reach the driver correctly; routing
to a chosen pin works; a second task on a busy counter is refused, including a
counter reading; 0 %, 100 % and zero frequency are refused before the board is
touched.

**Not proven: no pulse has ever been observed.** The test board is simulated,
cannot read outputs back, and does not simulate counter timing. Continuous vs.
finite, the pulse count, and call order are covered by tests only.

## Driving the digital lines

A **line** is on or off; a **port** is the number its lines form.

| Write to | With | What happens |
|---|---|---|
| `/nidaqmx/Dev1/port0/line3` | `true` / `false` | that one line goes high or low |
| `/nidaqmx/Dev1/port0` | `41381`, or `"0xA5A5"` | **every line of the port at once**, bit 0 = line 0 |
| `/nidaqmx/Dev1/do3` | `true` / `false` | the old spelling of `port0/line3`; still works |

- Every port and every line is in the command list.
- A port write is one clock tick, so a valve manifold's pattern changes
  together.
- Hex goes in quotes (`"0xA5A5"`), since JSON has no hex. `"0b1010_0101"` and
  plain numbers work too.
- **A value too wide for the port is refused.** A 16-line port takes at most
  65535. The driver itself would silently drop the extra bits; Phloem refuses
  and names the port and the widest value.

### Patterns the board clocks out

`command.pattern` gives the board a list of states and a rate, and the board
steps through them:

```
command.pattern { path: "/nidaqmx/Dev1/port0", samples: ["0x0001","0x0002","0x0004"], rate_hz: 1000 }
```

Like a ramp (see **Ramps the board clocks out**):

- It is **finite**, never repeated.
- It is **limited to 60 seconds**. For longer, drive the lines with ordinary
  writes.
- It is **one at a time**; a board has one digital timing engine.
- **When it ends, the lines hold the last state**, and Phloem writes that state
  again so it is recorded.

Stopping part way reports how many states the board got through, and what the
lines now hold.

**Making it wait for something.** Add `trigger` and the board holds the pattern
until an edge arrives on that terminal, then starts within one clock tick:

```
command.pattern {
  path: "/nidaqmx/Dev1/port0",
  samples: ["0x01", "0x03", "0x02", "0x00"],
  rate_hz: 1000,
  trigger: "/Dev1/PFI0"
}
```

Use `{ "source": "/Dev1/PFI0", "edge": "falling" }` for a falling edge; a bare
terminal means rising. Pair it with **Put the start trigger on** above, and the
pattern starts with the recording. Without a trigger, the pattern starts when
the command arrives, which has no guaranteed latency.

`command.status` says `waiting_for` while armed and not yet fired.
`command.stop` disarms it, even before it fires.

**Not proven:** NI's simulated boards do not simulate triggering, so an armed
pattern starts at once there. A terminal the board cannot route is refused.

### The dead-man and a digital pattern

Here the dead-man guards the very lines being driven, so:

- **An ordinary digital write is never refused**, even on a guarded line. That
  is what the dead-man is for.
- **A pattern on lines the dead-man does not guard is allowed.** A dead-man on
  `do0` says nothing about `port1`.
- **A pattern that shares lines with the dead-man is refused, naming those
  lines.** Stop the pattern, disarm the dead-man, or use other lines.

The reason: **nobody knows which one wins the pins** when the dead-man fires
during a pattern. The driver accepts both, and simulated boards never expire.
Until it is measured on real hardware, the overlap is refused.

### What has been proven, and what has not

**Proven** on the real driver: a whole port and a single line are written the
same way; every line is reachable; the board reports a 16-line port that can be
clocked; a pattern of 2000 states at 1 kHz **took 2.014 seconds**, so the board
really paces it; stopped 400 ms into a 5-second pattern, it reported 392 states
done.

**Not proven: no line has ever been observed**, since a simulated board cannot
read its outputs back. What a dead-man expiry does to a running pattern is also
unknown, so that combination is refused.

## Is it alive, and making it behave

Two operations for the **board** as a whole.

### Self-test

**Self-test** runs NI's own brief check of the board and reports exactly what
the driver said: pass or fail, plus its message. It changes nothing.

A pass reads like this:

    device        Dev1
    product_type  USB-6421
    passed        true
    status        0
    message       —

A failure keeps the driver's sentence, which NI's documentation refers to:

    passed        false
    status        -200126
    message       DAQmx error -200126: The device self-test failed.

If another program has the device open (FlexLogger, LabVIEW, another Phloem
session), the self-test cannot run, and you get an error saying so. That is
not a failed board: close the other program.

**A simulated board always passes**, and the answer says so whenever the board
is simulated.

### Reset

**Reset** returns the board to its power-on state, for when the card is wedged.
It is the most destructive thing this connector can do:

- **Every task on the board stops**, in every program on the machine. Any
  recording from this board must be restarted. A LabVIEW or FlexLogger session
  using it loses it too.
- **Every output goes to its power-on value**, not your last command and not
  any safe state. A valve held at 30 % is no longer held.
- **All signal routing is cleared**, so anything following an exported clock
  or trigger stops.

Only a person or a Trellis script can reset, never the assistant.

**Before the reset**, Phloem stops any ramp, pulse train or pattern it was
running **on that board**, and lists them in the answer. Other boards are left
alone. Phloem also forgets the last voltage it commanded on that board, so the
next ramp asks where to start.

**A reset is refused while the dead-man is armed.** A reset clears the timer
and drops the lines to power-on states, leaving the stand unguarded with
nothing looking wrong. **Disarm the dead-man first.**

### What has been proven, and what has not

**Proven** on the real driver: the self-test runs and answers (status 0 on a
simulated `Dev1`, with the simulated caveat), and **a reset really aborts a
running task** — the next read returned `-88709: … a task has been aborted or a
device has been removed from the system`.

**Not proven** on hardware, covered by tests only: a failing self-test, the
"device in use" case, and that the board's real name reaches the driver.

## IEPE, bridge and 4-20 mA out — what has been proven

These have **never been run against an instrument**:

* IEPE **accelerometer** and **microphone** inputs (`accel`, `microphone`)
* **bridge** inputs — load cells, pressure transducers, torque cells (`bridge`)
* **4-20 mA current output** (`ao0=current:0.004:0.02`)
* **analog triggering** — both `level=` and `window=`
* **LVDT / RVDT position** and **IEPE force** inputs (`lvdt`, `rvdt`,
  `force_iepe`). These are one step further back: no available board offers
  them, so testing shows only the board's refusal.

The first four are set up and checked by NI-DAQmx on a *simulated* board of
the right type, which reports every setting back. That is more than "the board
refused it", but it is not a correct reading.

**What has been checked** (NI-DAQmx 26.5.0):

| feature | board | what the driver reports back |
|---|---|---|
| IEPE accelerometer | USB-9234 | accelerometer; **AC coupling**; internal excitation; 100 mV/g; units of g — and it streams |
| IEPE microphone | USB-9234 | microphone; AC coupling; 50 mV/Pa; 100 dB SPL |
| bridge | USB-9237, USB-9219 | bridge; full bridge; internal 2.5 V excitation; 350 Ω; units of V/V |
| 4-20 mA out | USB-9265 | current output; range 0.004 to 0.02 |
| analog trigger | PCIe-6363 | level and window both accepted; the window edges come back rounded to the board's own trigger resolution |

- The driver selects AC coupling for IEPE itself, unasked.
- The trigger's window edges come back *rounded*, so they reached real trigger
  circuitry.
- Each call matches NI's documented entry point and arguments, and is pinned
  by tests.
- A board that **cannot** do these refuses by name, before recording.

**Check your excitation.** Ask a **USB-9234** for 4 mA of IEPE excitation (NI's
default) and it silently gives **2 mA**; 10 or 20 mA also give 2 mA. A
**USB-4431** refuses and lists the two values it accepts. If excitation matters
to your sensor, **state it** (`excit=0.002`) and check the module's datasheet.

**Not checked — needs a real instrument:**

* **That the numbers are right.** A simulated board outputs a test waveform
  whatever the settings. A wrong constant would give readings off by a factor,
  in the right unit.
* **That the excitation actually flows.**
* **That 0.02 is twenty milliamps** into a real loop, or that the board's
  compliance voltage suits your loop.
* **That a trigger fires when it should.** NI does not simulate triggering.
* **That `bridge=quarter` matches** the arm your gauge is actually in.

**In short:** if your board supports one of these, the connector will set it
up, and it may well be right first time. Check the first readings against
something you trust. If your board does not support it, you will be told.

## Simulated boards

NI MAX can create a **simulated** board that exists only in software. It
answers like a real board and gives a clean sine wave on every input. That is
useful before hardware arrives, and a trap after, because the numbers look
reasonable.

So Phloem labels it everywhere it names the board: the board picker,
**Test connection**, and the connector's log. For example: `Dev1 — USB-6421 (16
AI channels) · SIMULATED — no hardware attached; readings are synthetic`. A
real board shows nothing extra. If NI-DAQmx is too old to say, Phloem says
that instead.

On a simulated board, as NI documents:

- **Triggering is not simulated.** A triggered task starts at once.
- Counters always read zero.
- **Digital ports count up** — `0, 257, 514, 771, …` — however you sample
  them, so change detection fires on that.

## Notes

- Readings are spaced by the board's sample clock. Each block is anchored to
  this computer's clock when it is read, so it can sit up to one read interval
  off absolute time. A chassis on network time anchors on its own clock (see
  **A chassis on network time**).
- For engineering units, see **Turning a reading into engineering units**: wire
  the sensor as what it is if you can, use **Calibration** if you cannot, and
  use **Channel scaling (at source)** only when the unit must be true at the
  board. Software thermocouple conversion (`tc_k:`) is refused here; the board
  does that itself.
- The outputs offered are the ones the board has: its analog outputs, at the
  range you set (or the board's limits if none), and its digital lines. A board
  with no analog output offers none.
- Every output write is recorded.
- A setpoint is a **command**, not a safety-rated action. Its timing has no
  upper bound. Anything where a late write is a hazard belongs on a PLC or
  FPGA, with the hardware dead-man as the backstop.
- Write a digital line with its port: `port0/line3`. `line3` alone is refused.
- Without the NI-DAQmx runtime, the connector says so.

## Connection fields

| Field | Type | Required | Notes |
|---|---|---|---|
| Device | Text | Yes | The board, as NI-DAQmx names it. Pick it from the list — it is read off this machine. |
| Label | Text | No |  |
| Channel paths | One of | No |  |
| Channels | Text | No | What to stream. Tick the inputs, counters and digital lines in the table below. |
| Sample rate | Text | Yes | A level (slow, medium, fast) or a number of hertz. Modules without their own rate run at this one. |
| Input range | One of | No | The volts the analog inputs are read on — the board's own list. A smaller range that still contains the signal gives more resolution. |
| Sensors (per channel) | Text | No | What is wired to each input, and how. Set it on the channel row. |
| Rate levels | Text | No | The hertz behind each level. Set it with Rate levels in the setup. |
| Rates (per module) | Text | No | Each module's level — slow, medium or fast. Set it on the module's slot row. |
| Counters (per counter) | Text | No | What each counter you ticked measures. Set it on the counter row. |
| Sample clock | One of | No | What paces the samples. The board's own clock, or a pin another instrument drives it from. |
| Sample on | One of | No |  |
| Run | One of | No | A reference trigger (below) needs a set number: 'how much after the event' is the rest of that count. |
| Samples per channel | Number | No |  |
| Start | One of | No | What starts the recording. Only what this board can do is offered. |
| On pin | One of | No |  |
| On input | One of | No |  |
| Edge | One of | No |  |
| Level (V) | Number | No |  |
| Band bottom (V) | Number | No |  |
| Band top (V) | Number | No |  |
| Fire when the signal is | One of | No |  |
| Keep samples from before an event | One of | No | A reference trigger has been recording all along and keeps what led up to the event. Needs 'Run: for a set number of samples'. |
| Event on pin | One of | No |  |
| Event on input | One of | No |  |
| Edge | One of | No |  |
| Level (V) | Number | No |  |
| Band bottom (V) | Number | No |  |
| Band top (V) | Number | No |  |
| Fire when the signal is | One of | No |  |
| Samples to keep from before | Number | No | There is no default — this number is the reason to use a reference trigger. |
| Pause while a pin is | One of | No |  |
| …is | One of | No |  |
| Digital lines are sampled | One of | No |  |
| Rising edges on | Text | No |  |
| Falling edges on | Text | No |  |
| Inverted lines | Text | No | Digital lines read inverted, joined by +, as Channels names them (port0/line3+port1). |
| Put the sample clock on | One of | No | So a second instrument can follow this one. |
| Put the start trigger on | One of | No |  |
| Put the reference trigger on | One of | No |  |
| Clock source board | One of | No | The board whose clock and start the others follow — the end of the wire the signal comes OUT of. |
| Sample clock wire on pin | One of | No | The same pin name at both ends: out on the clock source board, in on every other board. |
| Start trigger wire on pin | One of | No | A second wire. A shared rate without a shared start is a constant, invisible offset. |
| Analog output range (per channel) | Text | No | What each analog output may drive. Set it on the output row. |
| Counter output idle state (per counter) | Text | No | Where each counter's output pin rests when it is NOT pulsing. Set it on the counter's output row. |
| Dead-man timeout (s) | Number | No | How long the outputs may hold after this computer stops answering, before the CARD drives them to the safe states set on the output rows. Empty = no dead-man. |
| Dead-man safe states (per line) | Text | No | What each digital output is driven to when the dead-man fires. Set it on the output row. |
| Scaling on the device (per channel) | Text | No | Only for hardware that must scale before it emits, or when the unit has to be true at the BOARD. Otherwise set a calibration in the channel's dialog. |
