Skip to content

Bitbus Function Block

Features

The SIO07 has a Bitbus sniffer function block to capture Bitbus frames on the Bitbus and provide them as a stream of frames to the host application.

The Bitbus sniffer operates listen-only, i.e. can only read from the bus, it cannot send any data, simply because the hardware has no transmitter.

Features:

  • Timestamping of received data with microsecond resolution
  • Capturing of all SDLC frames on the Bitbus
  • Filtering of Bitbus frames by address and minimal length
  • 62.5 and 375 kBaud supported

Functional Description

The Bitbus sniffer firmware permanently captures all frames from both lines of the Bitbus.

The captured frames contain the SDLC frame data: Address, Control and Information field. The CRC field is not included in the frame data.

Whether frames with CRC error are captured or discarded can be configured by the host application.

Baudrate and filter settings are configured by the host application.

Multiple applications may be connected simultaneously to the same Bitbus sniffer.

Using the io4edge API to access the Bitbus Sniffer

Info

At the moment, only the Python API is supported.

First, install the io4edge client library.

Want to have a quick look to the examples? See our Github repository.

Connect to the Bitbus Sniffer Function

To access the Bitbus Sniffer Function, create a Client and save it to the variable bitbus_client. Pass as address either a service address or an ip address with port. Examples:

  • As a service address: SIO07-1-bitbus
  • As an IP/Port: 192.168.201.1:10001

We need this client variable for all further access methods.

import io4edge_client.bitbussniffer as bbsniffer
import io4edge_client.functionblock as fb

def main():
  bb_client = bbsniffer.Client(address)

Configuring the Bitbus Sniffer

With the configuration upload function, the Bitbus Sniffer can be configured. The following example configures the Bitbus Sniffer to ignore CRC errors, use 375kBaud, and only pass frames with addresses 0, 1, and 2. Furthermore, frames with length 0 are discarded.

    addr_filter = bytearray([0x00] * 32)
    addr_filter[0] = 0x07  # only pass addresses 0,1,2, all others blocked

    bb_client.upload_configuration(
        bbsniffer.Pb.ConfigurationSet(
            ignore_crc=True,
            baud_62500=False,
            address_filter=bytes(addr_filter),
            min_frame_length=1, # Discard frames with 0 length
        )
    )

Receiving Bitbus Frames

Start a Stream

To receive frames from the Bitbus, the API provides functions to start a Stream. When starting the stream, you can specify a filter to receive only frames that match the filter criteria.

The following code shows a typical filter setting: Receive all frames with all FCodes, but no timed out frames.

   bb_client.start_stream(
        bbsniffer.Pb.StreamControlStart(),
        fb.Pb.StreamControlStart(
            bucketSamples=20,
            keepaliveInterval=1000,
            bufferedSamples=60,  # Minimum frames with max. length to buffer. If frames are small, much more frames are buffered
        ),
    )

Receive Telegrams

In the stream the firmware generates Buckets, where each Bucket contains a number of Frames. The frame contains the timestamp, the Bitbus frame data, and flags (e.g. CRC error). Within the bitbus frame data, the first byte is the Address, the second byte is the Control field, and the remaining bytes are the Information field.

To read samples from the stream:

    while True:
        try:
            generic_stream_data, stream_data = bb_client.read_stream(timeout=3)
        except TimeoutError:
            print("Timeout while reading stream")
            continue

        print(
            "Received %d samples, seq=%d, ts=%d"
            % (
                len(stream_data.samples),
                generic_stream_data.sequence,
                generic_stream_data.deliveryTimestampUs,
            )
        )

        for sample in stream_data.samples:
            print(sample_to_str(sample))


def sample_to_str(sample):
    ret_val = "%10d us: " % sample.timestamp
    if len(sample.bitbus_frame) >= 2:
        ret_val += f"ADDR: 0x{sample.bitbus_frame[0]:02X} "
        ret_val += f"CTRL: 0x{sample.bitbus_frame[1]:02X} "
        ret_val += "INFO: "
        for i in range(2, len(sample.bitbus_frame)):
            ret_val += "%02X " % sample.bitbus_frame[i]

    if sample.flags != bbsniffer.Pb.Sample.Flags.none:
        if sample.flags & bbsniffer.Pb.Sample.Flags.bad_crc:
            ret_val += " CRC_ERR"
        if sample.flags & bbsniffer.Pb.Sample.Flags.frames_lost:
            ret_val += " LOST"
        if sample.flags & bbsniffer.Pb.Sample.Flags.buf_overrun:
            ret_val += " BUF_OVERRUN"

    return ret_val

Info

At the moment, timestamps are expressed in microseconds relative to the start of the SIO07. Future client libraries will map the time to the host's time domain.

Controlling the Stream

The stream behavior can be fine-tuned to the application needs:

  • The bucketSamples parameter defines the number of samples per bucket. If the bucket contains bucketSamples, it is sent to the client.

  • The keepAliveInterval parameter defines the maximum time in ms between two buckets. If the bucket is not full, it is sent after the configured interval.

  • The bufferedSamples parameter defines the number of samples that can be buffered in the device. If the buffer is full, the oldest samples are overwritten. As a rule of thumb, bufferedSamples should be at least two times the bucketSamples. Select a higher number if your reception process is slow to avoid buffer overruns.

    bb_client.start_stream(
        bbsniffer.Pb.StreamControlStart(),
        fb.Pb.StreamControlStart(
            bucketSamples=20,
            keepaliveInterval=1000,
            bufferedSamples=60,
        ),
    )