Skip to content

Protocol decoders

deckerjulian edited this page Oct 6, 2026 · 3 revisions

Protocol decoders

openSciLab runs the unmodified pd.py decoders of libsigrokdecode in Python - about 130 of them, from UART, SPI and I²C to CAN, USB, 1-Wire, JTAG and SWD, plus mos6502 and the C64 bus decoder for retro computers. This page shows how to use them in the data view, how to read and export their output, how to add your own and how to use them in flows.

Adding a decoder

  1. Open a capture in a data view (or capture one) and open the Decoders tab of the analysis panels (Ctrl+1 shows the panels).
  2. Press Add decoder.... The dialog lists the decoders by category; type into the search field to find one by name or description, select it and press Add decoder.
  3. The settings dialog of the decoder opens:
    • Channels: a capture channel for each input of the decoder (* marks the required ones). Inputs whose name matches a channel name of the capture are assigned already - another reason to name your channels (SCL, RX, A0 (Y), ...).
    • Options: baud rate, word size, address format and whatever else the decoder has.
  4. Press Apply. The decoder runs and its rows appear above the time axis.

Decode automatically (on by default) decodes again whenever the capture or a decoder changes; Decode now does it on demand. A capture that streams in is decoded once it is complete, one recorded to disk only when you press Decode now. The decoders run in the background; the window stays usable meanwhile, and the line next to the title says what happened (3 annotation row(s), or 1 decoder(s) failed with the reason in its tool tip).

Try it without hardware: open examples/demo.lac from the repository (or the example Basics → Read a capture file, whose data folder holds the same file). It holds I²C on the channels SCL and SDA, a UART on RX and a clock. Add I²C and UART: both find their channels by name. The UART runs at its default 115200 baud; set its Data format to ascii to read the text LogicAnalyzer. Then select the I²C decoder and stack 24xx EEPROM (eeprom24xx) on it.

Managing decoders

The list on the Decoders tab holds the decoders of this data view (every data view has its own):

  • the check box turns a decoder off and on;
  • a double-click (or the settings button) opens its channels and options;
  • the buttons below the list stack a decoder on the selected one, open its settings and remove it;
  • (channels missing) marks a decoder whose channels are not in this capture, (not installed) one that is not found on this computer (e.g. from a profile made elsewhere).

Decoders are not saved in the capture file. To use a set of decoders again, save them in a profile: Profiles → Save current settings as profile... stores the capture settings together with the decoders, and loading the profile brings both back (see Data view → Profiles). The standard profiles for UART, I²C, SPI, CAN and the other buses add their decoders as well.

Stacked decoders

Some decoders decode the output of another one instead of channels, e.g. eeprom24xx on top of I²C, or a display driver on top of SPI.

  • Select the lower decoder in the list and press the stack button: the dialog lists only the decoders that take its output. The stacked one is shown indented (↳).
  • Add decoder... lists stacked decoders too, marked (decodes i2c) and so on. Choosing one stacks it on a decoder of the list it fits on (it asks if there are several) or offers to add the decoders it needs first ("Add I²C first and stack 24xx EEPROM on top?").

Reading the annotations

Each decoder adds one or more rows above the time axis, in its colour. Long rows zoomed out are drawn as blocks; zoom in to read the single entries.

  • Rest the pointer on an annotation to see its value. Its samples are marked over all channels, a dashed line shows the sample the decoder read the value at, and every channel it read shows its level. Numbered channels such as A0…A15 form a bus and show their value (A15 = 1 (+$8000), A = $FFFC); a level that changes right next to the read point is outlined as a warning. The entries of other rows that belong to it (the bus cycles of an instruction, for example) are outlined too.
  • Click the name of a row to open it as a list in a window of its own; a double-click on an annotation opens the list with that entry selected.

The list window shows Sample, Time (relative to the trigger), Duration, Type and Value of every entry, and the other rows of the decoder as further columns (Columns shows or hides them). Below it, the details of the selected entry. Selecting an entry shows it in the waveform; resting the pointer on an annotation in the waveform selects its entry.

  • The filter field keeps the entries whose type or value contains the text (JSR, $FD, ACK).
  • Copy (or Ctrl+C) copies the selected entries - all listed ones without a selection - as tab separated text; Copy values copies only the values, e.g. a plain disassembly listing.

The decoder output can also be searched (Search → Decoder output, with Regular expression and Match case) and listed below the waveform (Listing → Decoder output).

Exporting the decoder output

Project → Export → Decoder output (.csv, .json)... (with the data view active) writes every annotation. The CSV has the columns Start time (s), End time (s), Start sample, End sample, Decoder, Row and Value, with times relative to the trigger; the JSON file holds the same records, every text of an annotation and the errors of the decoders.

On the command line, openscilab-cli decode does the same without the window (see Scripting):

openscilab-cli decoders                       # every decoder with its id and channels
openscilab-cli decode capture.lac -D i2c:scl=SCL,sda=SDA -D eeprom24xx -o eeprom.json

Your own decoders

A decoder of your own is an ordinary sigrok decoder: a folder with __init__.py and pd.py (see the protocol decoder API). Copy the folder into one of the folders openSciLab searches - the easiest is decoders in the settings directory (see Files) - and start openSciLab again: decoders are loaded once at the start.

The folders are searched in this order; when two folders hold a decoder of the same name, the first one wins:

  1. folders given with openscilab --decoders <folder> (repeatable)
  2. the environment variable OPENSCILAB_DECODERS (several folders separated by :, on Windows by ;)
  3. decoders in the working directory
  4. decoders next to the openscilab package (when running from source)
  5. decoders inside the installed application (the decoders that come with it)
  6. decoders in the settings directory
  7. the folders of an installed libsigrokdecode (/usr/share/libsigrokdecode/decoders, Homebrew on macOS, PulseView on Windows)

Because the decoders that come with openSciLab are found before the settings directory, give your decoder a name of its own. Help → Decoder search paths... shows the folders and how many decoders were loaded; openscilab-cli decoders lists them. A decoder with an error in its pd.py is left out of the list: if yours is missing, check it with Python first.

Decoders are Python files that are executed. Only add decoders from sources you trust.

Decoders in flows

Every decoder is also a node of the flow editor: decode.uart, decode.i2c, decode.spi, decode.c64bus and so on (group Decoders in the node palette; see Nodes).

  • Input in: a capture (from device.capture, data.file_read, ...).
  • Outputs: events (an event per annotation), table (the annotations as rows with time, row and value) and text (the values of one row joined, e.g. the bytes a UART received).
  • Parameters: channels maps the decoder's inputs to channels of the capture ({rx: D9}); without it, channels named like the inputs are used (SCL, SDA). The decoder's options are parameters of their own (baudrate, format, ...); rows keeps only some rows, text_row chooses the row that becomes text.
  • Stacked decoders take the capture as well and run the decoder below them themselves: a decode.eeprom24xx node gets the channels of the I²C decoder ({scl: SCL, sda: SDA}) and sends the rows of both.
flow: UART
nodes:
  la: {type: device.instrument, address: "sim:free"}
  capture: {type: device.capture, channels: [D9], rate: 4 MHz, duration: 3 ms}
  uart: {type: decode.uart, channels: {rx: D9}, baudrate: 115200, format: ascii}
  text: {type: view.log, title: Received text}
edges:
  - la.device -> capture.device
  - capture.capture -> uart.in
  - uart.text -> text.in

The examples of the category Protocols (Templates → Protocols) decode UART, SPI and I²C from a simulator and check what a device sends. Flows and data views use the same decoders, from the same folders.

Clone this wiki locally