NI Counter Read Task
Learn how to acquire counter data from NI devices with Synnax.
For task lifecycle management, see the Task Basics page.
Task Configuration Reference
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | - | Human-readable task name |
sample_rate | number | Yes | - | Samples per second (Hz), determined by module |
stream_rate | number | No | sample_rate | Rate data is streamed to Synnax (Hz), must be ≤ sample_rate |
data_saving | boolean | No | false | Enable permanent storage in Synnax |
auto_start | boolean | No | false | Automatically start task after configuration |
channels | array | Yes | - | List of counter input channel configurations |
Channel Types Reference
Frequency (ci_frequency)
ci_frequency)Measures the frequency of digital pulses.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
device | string | Yes | - | Device identifier |
port | number | Yes | - | Counter port (0-indexed) |
min_val | number | No | 2 | Minimum frequency |
max_val | number | No | 100 | Maximum frequency |
units | enum | No | Hz | Hz, Ticks |
edge | enum | No | Rising | Rising, Falling |
meas_method | enum | No | DynAvg | LowFreq1Ctr, HighFreq2Ctr, DynAvg |
meas_time | number | No | 0.000006 | Measurement time (seconds), used with HighFreq2Ctr |
divisor | number | No | 4 | Integer divisor, used with LargeRng2Ctr |
terminal | string | No | - | PFI terminal for signal routing |
custom_scale | object | No | - | Custom scaling |
Edge Count (ci_edge_count)
ci_edge_count)Counts rising or falling edges on a digital signal.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
device | string | Yes | - | Device identifier |
port | number | Yes | - | Counter port (0-indexed) |
active_edge | enum | No | Rising | Rising, Falling |
count_direction | enum | No | CountUp | CountUp, CountDown, ExternallyControlled |
initial_count | number | No | 0 | Starting count value |
terminal | string | No | - | PFI terminal for signal routing |
Period (ci_period)
ci_period)Measures the time between edges of a periodic signal.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
device | string | Yes | - | Device identifier |
port | number | Yes | - | Counter port (0-indexed) |
min_val | number | No | 0.000001 | Minimum period |
max_val | number | No | 0.1 | Maximum period |
units | enum | No | Seconds | Seconds, Ticks |
starting_edge | enum | No | Rising | Rising, Falling |
meas_method | enum | No | DynAvg | LowFreq1Ctr, HighFreq2Ctr, DynAvg |
meas_time | number | No | 0.001 | Measurement time (seconds), used with HighFreq2Ctr |
divisor | number | No | 4 | Integer divisor, used with LargeRng2Ctr |
terminal | string | No | - | PFI terminal for signal routing |
custom_scale | object | No | - | Custom scaling |
Pulse Width (ci_pulse_width)
ci_pulse_width)Measures the width of pulses in a digital signal.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
device | string | Yes | - | Device identifier |
port | number | Yes | - | Counter port (0-indexed) |
min_val | number | No | 0.000001 | Minimum pulse width |
max_val | number | No | 0.1 | Maximum pulse width |
units | enum | No | Seconds | Seconds, Ticks |
starting_edge | enum | No | Rising | Rising, Falling |
terminal | string | No | - | PFI terminal for signal routing |
custom_scale | object | No | - | Custom scaling |
Semi Period (ci_semi_period)
ci_semi_period)Measures the half-period of a periodic signal.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
device | string | Yes | - | Device identifier |
port | number | Yes | - | Counter port (0-indexed) |
min_val | number | No | 0.000001 | Minimum semi-period |
max_val | number | No | 0.1 | Maximum semi-period |
units | enum | No | Seconds | Seconds, Ticks |
custom_scale | object | No | - | Custom scaling |
Two Edge Separation (ci_two_edge_sep)
ci_two_edge_sep)Measures the time between two different edges of a signal.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
device | string | Yes | - | Device identifier |
port | number | Yes | - | Counter port (0-indexed) |
min_val | number | No | 0.000001 | Minimum separation time |
max_val | number | No | 1 | Maximum separation time |
units | enum | No | Seconds | Seconds, Ticks |
first_edge | enum | No | Rising | Rising, Falling |
second_edge | enum | No | Falling | Rising, Falling |
custom_scale | object | No | - | Custom scaling |
Velocity Linear (ci_velocity_linear)
ci_velocity_linear)Measures linear velocity using a quadrature encoder.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
device | string | Yes | - | Device identifier |
port | number | Yes | - | Counter port (0-indexed) |
min_val | number | No | 0 | Minimum velocity |
max_val | number | No | 1 | Maximum velocity |
units | enum | No | m/s | m/s, in/s |
decoding_type | enum | No | X4 | X1, X2, X4, TwoPulse |
dist_per_pulse | number | No | 0.001 | Distance traveled per encoder pulse |
terminal_a | string | No | - | PFI terminal for encoder A signal |
terminal_b | string | No | - | PFI terminal for encoder B signal |
custom_scale | object | No | - | Custom scaling |
Velocity Angular (ci_velocity_angular)
ci_velocity_angular)Measures angular velocity (rotational speed) using a quadrature encoder.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
device | string | Yes | - | Device identifier |
port | number | Yes | - | Counter port (0-indexed) |
min_val | number | No | 0 | Minimum angular velocity |
max_val | number | No | 1 | Maximum angular velocity |
units | enum | No | RPM | RPM, Radians/s, Degrees/s |
decoding_type | enum | No | X4 | X1, X2, X4, TwoPulse |
pulses_per_rev | number | No | 24 | Number of encoder pulses per revolution |
terminal_a | string | No | - | PFI terminal for encoder A signal |
terminal_b | string | No | - | PFI terminal for encoder B signal |
custom_scale | object | No | - | Custom scaling |
Position Linear (ci_position_linear)
ci_position_linear)Tracks linear position using a quadrature encoder.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
device | string | Yes | - | Device identifier |
port | number | Yes | - | Counter port (0-indexed) |
units | enum | No | Meters | Meters, Inches, Ticks |
decoding_type | enum | No | X4 | X1, X2, X4, TwoPulse |
dist_per_pulse | number | No | 0.000001 | Distance traveled per encoder pulse |
initial_pos | number | No | 0.0 | Starting position |
terminal_a | string | No | - | PFI terminal for encoder A signal |
terminal_b | string | No | - | PFI terminal for encoder B signal |
z_index_enable | boolean | No | false | Enable Z-index signal |
z_index_val | number | No | 0 | Position value when Z-index pulse occurs |
z_index_phase | enum | No | AHighBHigh | AHighBHigh, AHighBLow, ALowBHigh, ALowBLow |
terminal_z | string | No | - | PFI terminal for Z-index signal |
custom_scale | object | No | - | Custom scaling |
Position Angular (ci_position_angular)
ci_position_angular)Tracks angular position (rotation angle) using a quadrature encoder.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
device | string | Yes | - | Device identifier |
port | number | Yes | - | Counter port (0-indexed) |
units | enum | No | Degrees | Degrees, Radians, Ticks |
decoding_type | enum | No | X4 | X1, X2, X4, TwoPulse |
pulses_per_rev | number | No | 24 | Number of encoder pulses per revolution |
initial_angle | number | No | 0.0 | Starting angle |
terminal_a | string | No | - | PFI terminal for encoder A signal |
terminal_b | string | No | - | PFI terminal for encoder B signal |
z_index_enable | boolean | No | false | Enable Z-index signal |
z_index_val | number | No | 0 | Angle value when Z-index pulse occurs |
z_index_phase | enum | No | AHighBHigh | AHighBHigh, AHighBLow, ALowBHigh, ALowBLow |
terminal_z | string | No | - | PFI terminal for Z-index signal |
custom_scale | object | No | - | Custom scaling |
Duty Cycle (ci_duty_cycle)
ci_duty_cycle)Measures the duty cycle of a periodic signal.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
device | string | Yes | - | Device identifier |
port | number | Yes | - | Counter port (0-indexed) |
min_val | number | No | 2 | Minimum duty cycle frequency |
max_val | number | No | 10000 | Maximum duty cycle frequency |
active_edge | enum | No | Rising | Rising, Falling |
terminal | string | No | - | PFI terminal for signal routing |
custom_scale | object | No | - | Custom scaling |
Important Rules
- Sample rates: All channels in a task sample at the same rate. Create separate tasks for different rates.
- One task per module: Only one running task can claim a module at a time.
- Stream rate optimization: For low-rate tasks (< 50 Hz), set the stream rate to the sample rate. For high-rate tasks, keep stream the rate < 50 Hz for better performance.
- Port validation: Counter ports cannot be duplicated within the same task.
How-To
Configure and run task
import synnax as sy
from synnax.hardware import ni
client = sy.Synnax()
# Retrieve device
counter_dev = client.hardware.devices.retrieve(name="Mod1_Counter")
# Create index channel
ctr_time = client.channels.create(
name="ctr_time",
is_index=True,
data_type=sy.DataType.TIMESTAMP,
retrieve_if_name_exists=True,
)
# Create data channels
frequency_ch = client.channels.create(
name="frequency",
index=ctr_time.key,
data_type=sy.DataType.FLOAT32,
retrieve_if_name_exists=True,
)
edge_count_ch = client.channels.create(
name="edge_count",
index=ctr_time.key,
data_type=sy.DataType.UINT32,
retrieve_if_name_exists=True,
)
angular_pos_ch = client.channels.create(
name="angular_position",
index=ctr_time.key,
data_type=sy.DataType.FLOAT32,
retrieve_if_name_exists=True,
)
# Create and configure task
task = ni.CounterReadTask(
name="Counter Read Task",
sample_rate=sy.Rate.HZ * 100,
stream_rate=sy.Rate.HZ * 25,
data_saving=True,
channels=[
ni.CIFrequencyChan(
channel=frequency_ch.key,
device=counter_dev.key,
port=0,
min_val=1,
max_val=10000,
units="Hz",
edge="Rising",
meas_method="DynamicAvg",
),
ni.CIEdgeCountChan(
channel=edge_count_ch.key,
device=counter_dev.key,
port=1,
active_edge="Rising",
count_direction="CountUp",
initial_count=0,
),
ni.CIPositionAngularChan(
channel=angular_pos_ch.key,
device=counter_dev.key,
port=2,
units="Degrees",
decoding_type="X4",
pulses_per_rev=1024,
initial_angle=0.0,
z_index_enable=False,
),
],
)
client.hardware.tasks.configure(task)
# Start task and read data
with task.run():
with client.open_streamer(["frequency", "edge_count", "angular_position"]) as streamer:
for _ in range(10):
frame = streamer.read()
print(frame) Edit task configuration
# Retrieve existing task
task = client.hardware.tasks.retrieve(name="Counter Read Task")
task = ni.CounterReadTask(internal=task)
# Update task-level configuration
task.config.auto_start = True
task.config.stream_rate = int(sy.Rate.HZ * 50)
# Update frequency channel configuration
task.config.channels[0].min_val = 10
task.config.channels[0].max_val = 5000
task.config.channels[0].meas_method = "LowFreq1Ctr"
task.config.channels[0].terminal = "PFI0"
# Update edge count channel configuration
task.config.channels[1].active_edge = "Falling"
task.config.channels[1].initial_count = 100
task.config.channels[1].terminal = "PFI1"
# Update angular position channel configuration
task.config.channels[2].pulses_per_rev = 2048
task.config.channels[2].initial_angle = 90.0
task.config.channels[2].z_index_enable = True
task.config.channels[2].terminalA = "PFI7"
task.config.channels[2].terminalB = "PFI8"
task.config.channels[2].terminalZ = "PFI9"
# Apply changes
client.hardware.tasks.configure(task) Configure and run task
import { Synnax } from "@synnaxlabs/client";
const client = new Synnax();
// Retrieve device
const counterDev = await client.hardware.devices.retrieve({ name: "Mod1_Counter" });
// Create index channel
const ctrTime = await client.channels.create({
name: "ctr_time",
isIndex: true,
dataType: "timestamp",
retrieveIfNameExists: true,
});
// Create data channels
const frequencyCh = await client.channels.create({
name: "frequency",
index: ctrTime.key,
dataType: "float32",
retrieveIfNameExists: true,
});
const edgeCountCh = await client.channels.create({
name: "edge_count",
index: ctrTime.key,
dataType: "uint32",
retrieveIfNameExists: true,
});
const angularPosCh = await client.channels.create({
name: "angular_position",
index: ctrTime.key,
dataType: "float32",
retrieveIfNameExists: true,
});
// Create and configure task
const task = await client.hardware.tasks.create({
name: "Counter Read Task",
type: "ni_counter_read",
config: JSON.stringify({
sample_rate: 100,
stream_rate: 25,
data_saving: true,
channels: [
{
type: "ci_frequency",
channel: frequencyCh.key,
device: counterDev.key,
port: 0,
min_val: 1,
max_val: 10000,
units: "Hz",
edge: "Rising",
meas_method: "DynamicAvg",
},
{
type: "ci_edge_count",
channel: edgeCountCh.key,
device: counterDev.key,
port: 1,
active_edge: "Rising",
count_direction: "CountUp",
initial_count: 0,
},
{
type: "ci_position_angular",
channel: angularPosCh.key,
device: counterDev.key,
port: 2,
units: "Degrees",
decoding_type: "X4",
pulses_per_rev: 1024,
initial_angle: 0.0,
z_index_enable: false,
},
],
}),
});
// Start task
await task.executeCommandSync("start");
// Read data
const streamer = await client.openStreamer([
"frequency",
"edge_count",
"angular_position",
]);
for (let i = 0; i < 10; i++) {
const frame = await streamer.read();
console.log(frame);
}
// Stop task
await task.executeCommandSync("stop");
await streamer.close(); Edit task configuration
// Retrieve existing task
const task = await client.hardware.tasks.retrieve({ name: "Counter Read Task" });
// Parse and update configuration
const config = JSON.parse(task.config);
// Update task-level configuration
config.auto_start = true;
config.stream_rate = 50;
// Update frequency channel configuration
config.channels[0].min_val = 10;
config.channels[0].max_val = 5000;
config.channels[0].meas_method = "LowFreq1Ctr";
config.channels[0].terminal = "PFI0";
// Update edge count channel configuration
config.channels[1].active_edge = "Falling";
config.channels[1].initial_count = 100;
config.channels[1].terminal = "PFI1";
// Update angular position channel configuration
config.channels[2].pulses_per_rev = 2048;
config.channels[2].initial_angle = 90.0;
config.channels[2].z_index_enable = true;
config.channels[2].terminalA = "PFI7";
config.channels[2].terminalB = "PFI8";
config.channels[2].terminalZ = "PFI9";
// Apply changes
await client.hardware.tasks.create({
key: task.key,
name: task.name,
type: task.type,
config: JSON.stringify(config),
});