Skip to content
deckerjulian edited this page Oct 7, 2026 · 2 revisions

Nodes

An overview of the node families of a flow: what each family is for and one or two typical uses. The complete list with every node is in docs/lab.md; in the application the palette shows a node's description as a tooltip, and the inspector shows it above the parameters.

Finding your way

The node palette beside the flow groups the nodes by family. A node's type names its family: measure.frequency is in Measurement.

Palette group Types For
Devices device.* the instrument itself, capturing, streaming, reading pins periodically
Remote devices remote.* devices on another computer (values, outputs, commands, clock)
Time timing.* sync signals, aligning instruments, measuring latency
GPIO gpio.* setting and reading pins, pulses, PWM, analog outputs
Generator gen.* waveforms, patterns, replaying captures, sending UART/SPI/I²C
Decoders decode.* the 133 sigrok protocol decoders
Measurement measure.* frequency, period, duty cycle, counts, min/max/mean/RMS, setup/hold
Signal processing dsp.* thresholds, filters, FFT, derivative, integral, averaging, formulas
Control control.* timers, sweeps, sequences, state machines, comparisons, Python code
Data data.* files, long-term logging, tables, buffers
Views view.* scope, strip chart, XY chart, spectrum, number, LED, table, log
Report report.* sections, tables, diagrams and checks of a report
Conversions convert.* one type of value into another
Structure structure.* bundles of values, comments, groups

Many nodes that need a device have a device input: wire it to the device output of a device node. Parameters take quantities with units (1 kHz, 20 ms, 3.3 V).

Devices

device.instrument is the instrument (its address: sim:uno, pico:COM5, ...; for a simulator signals says what it simulates). device.capture captures once with channels, rate, length and trigger, or again on every value at arm; with a clock channel it samples on the edges of that clock (state mode). device.stream sends blocks of samples while it runs. device.monitor reads pins and analog inputs a few times a second.

  • Capture on a trigger: device.capture with trigger: {edge: rising, source: D8}, wired to a scope and a measurement (Templates → Basics → Edge trigger).
  • Watch something slow: device.monitor on an analog input into a strip chart (Templates → Digital I/O → Monitor).

GPIO

gpio.write, gpio.read, gpio.pulse (pulses timed by the device), gpio.pwm and gpio.dac (voltage of an analog output).

  • Blink: a control.timer ticks, a counter and dsp.math turn the ticks into 1, 0, 1, ... for gpio.write (Templates → Digital I/O → Blink).
  • Stimulus: the capture reports armed, a control.sequence waits 1 ms and triggers gpio.pulse, so the capture sees the pulses (Templates → Digital I/O → Pulse train).

Generator

gen.waveform (sine, square, triangle, ramp, pulse, DC, noise), gen.arbitrary (a formula, a CSV file or a signal), gen.pattern (digital patterns in SDL), gen.replay (play a capture), gen.output (a saved *.wave.yaml), gen.tx_uart, gen.tx_spi, gen.tx_i2c. Their sync output marks each start, e.g. to arm a capture.

  • Function generator: gen.waveform on an oscilloscope's generator, captured on CH1 (Templates → Signal generation → Function generator).
  • Talk to a device: gen.tx_i2c reads a sensor with the board's own I²C (Templates → Protocols → I²C sensor).

Decoders

Every sigrok protocol decoder is a node (decode.uart, decode.i2c, decode.spi, decode.can, ...): a capture at in; the annotations as events, a table and text at the outputs. The inspector assigns the decoder's channels from the channels of the capture wired to it. A decoder that works on another one's output (eeprom24xx on i2c) is wired behind it.

  • Check a message: decode.uart → text → control.compare (Templates → Protocols → Check a message).

More in Protocol decoders.

Measurement

measure.frequency, period, duty, pulse_width, count, setup_hold for digital signals; measure.min, max, mean, rms, peak_to_peak for analog ones (each block of a stream gives a value).

  • Is the clock right? measure.frequency on capture.D8 → report.check with limits (Templates → Measurement → Frequency and duty cycle).
  • Scope measurements: mean, RMS and peak to peak of four oscilloscope channels (Templates → Analog → Scope measurements).

Signal processing

dsp.threshold (analog to digital, with hysteresis), dsp.debounce, dsp.filter (low-pass, high-pass, band-pass, band-stop: FIR or IIR), dsp.fft, dsp.derivative, dsp.integral, dsp.average, dsp.resample and dsp.math (an expression of the inputs a to d with numpy, e.g. sqrt(a**2 + b**2)).

  • Clean a noisy signal: dsp.filter before a measurement (Templates → Analog → Low pass filter).
  • A bouncing button: count edges before and after dsp.debounce (Templates → Digital I/O → Button).

Control

control.timer ticks, control.sweep steps a value, control.sequence runs steps without code, control.state_machine runs states with their outputs and ways out, control.compare and control.limit decide, control.counter counts, control.python runs code of your own.

  • Characteristic curve: control.sweep steps the duty cycle of gpio.pwm, gpio.read reads the voltage after each step, data.table and view.xy collect and draw the curve (Templates → Control → PWM sweep).
  • Traffic light: control.state_machine drives three pins (Templates → Control → Traffic light).

Data

data.file writes what arrives (captures as .lac, .sr, .csv, .vcd by the extension; tables and other values as CSV; numbered for one file per value), data.file_read reads a capture or table, data.logger records for hours to CSV or a capture on disk, data.table collects values into rows, data.buffer keeps the latest values.

  • Log readings: values into data.logger with a .csv path (Templates → Data and reports → CSV logger).
  • Work on a recording: data.file_read instead of a device (Templates → Basics → Read a capture file).

Views

view.scope (captures in a data view, with decoders and cursors), view.strip_chart, view.xy, view.spectrum, view.number, view.led, view.table, view.log. Each opens a document while the flow runs. For a front panel with the same displays use a panel.

  • Frequency over time: a stream, a measurement, view.strip_chart (Templates → Basics → Stream).

Report

report.section, report.table, report.image (diagrams), report.check (limits or an expected value) and report.write (HTML or PDF). See Panels and reports.

Conversions

convert.channel (one channel of a capture), convert.edges, convert.state_bit, convert.to_analog, convert.to_scalar. You rarely add them by hand: a wire whose types do not fit offers the right conversion (Flows).

Structure

structure.bundle puts several values on one wire, structure.unbundle takes them apart again; structure.comment and structure.group are notes and frames on the canvas. A subflow is a part of a flow used like one node (type subflow.<name>).

  • Two values together: frequency and duty cycle bundled into one CSV line (Templates → Data and reports → Bundle).

Time

timing.sync drives a sync signal on a pin, timing.align puts an instrument on a reference's time by that signal, timing.calibrate measures how late an instrument's samples arrive. See Time and synchronization.

Remote devices

remote.receive (values of a device on another computer, with the time they were measured), remote.set (an output, applied at a given time), remote.call (a command) and remote.sync (align its clock with a sync signal). They run in real time only. See Remote devices.

Nodes of your own

Inside a flow: control.python holds code. async def run(ctx) runs with the flow (ctx.emit(port, value), await ctx.sleep(seconds), await ctx.receive(port), ctx.device(name), ctx.log(text)); def on_input(ctx, port, value) is called for every value on an input. Its parameters inputs and outputs name the ports. The editor highlights the code and completes ctx., np. and the port names (Templates → Python → Python node).

For a project: every function with @node in a .py file in the project's nodes/ folder is a node type in the palette:

from openscilab.lab import In, Out, Param, node


@node("example.double", inputs=[In("value", "Scalar", optional=False)], outputs=[Out("out", "Scalar")])
def double(value):
    return value.value * 2


@node("example.blink", outputs=[Out("level", "Bool")], params=[Param("period", "quantity", "1 s", "s")])
async def blink(ctx):
    for _ in range(3):
        ctx.emit("level", True)
        await ctx.sleep(ctx.quantity("period") / 2)
        ctx.emit("level", False)
        await ctx.sleep(ctx.quantity("period") / 2)

A plain function is called with the latest values of its inputs whenever one changes; its result goes to the output (a dict of values for several outputs, None for nothing). An async def taking ctx runs with the flow. For periodic work use await ctx.sleep_until(start + n * period), which keeps the period; await ctx.device_call(...) calls a device without holding up the other nodes. The example is Templates → Python → Own nodes.

For every flow and project: a plugin brings node types with the same @node - a .py file in the plugins folder of the settings directory, or an installed package (see Writing drivers). Its nodes appear in the palette in a group of their own (node_group("calib", "Calibration") names it), in every project, in flow files and on the command line; Help → Plugins… lists them. A flow that uses a node of a plugin that is not installed says so. A complete example with a two-point calibration and an NTC thermistor is examples/plugins/calibration_nodes.py; the details are in docs/drivers.md.

Clone this wiki locally