Skip to content

Detailed Description

Functional Description

The IOU09 has 8 analog inputs, grouped in 4 groups of 2 channels each. Each group has an integrated sensor power supply, separate connector and a separate galvanic isolation to the host system.

The two channels of a group are sampled simultaneously, so they must use the same sampling rate. However, the gain can be set individually per channel.

Supported sample rates

  • 61,0351 Hz (default)
  • 122,0703 Hz
  • 244,140 Hz
  • 488,281 Hz
  • 976,562 Hz
  • 1953,125 Hz
  • 3906,250 Hz

Note

Due to internal limitations, only one group can be sampled with the highest sample rate. The other groups must use a lower sample rate. However, it is possible to sample all groups with 1953,125 Hz or lower.

Trying to sample more than one group with 3906,250 Hz may result in firmware restarts or lost samples.

Supported gains

Each channel can be individually configured with one of the following gain values:

  • 1 (default)
  • 2
  • 4
  • 8
  • 16
  • 32
  • 64
  • 128

Measurement Types and Ranges

Each channel can measure either voltage or current. The measurement type is just defined by the wiring of the input connector.

Electrical characteristics:

  • Voltage input: 100 kOhm impedance
  • Current input: 50 Ohm shunt resistor to ground

Warning

Do not connect voltage and current input pin of the same channel at the same time.

The following table shows the nominal input ranges, for different gain settings:

Measurement Type Gain Nominal Input Range
Voltage - via Ux pin 1 +/-10 V
Current - via Ix pin 1 +/-20 mA
Voltage - via Ux pin 2 +/-5 V
Current - via Ix pin 2 +/-10 mA
Voltage - via Ux pin 4 +/-2.5 V
Current - via Ix pin 4 +/-5 mA
Voltage - via Ux pin 8 +/-1.25 V
Current - via Ix pin 8 +/-2.5 mA
Voltage - via Ux pin 16 +/-0.625 V
Current - via Ix pin 16 +/-1.25 mA
Voltage - via Ux pin 32 +/-0.3125 V
Current - via Ix pin 32 +/-0.625 mA
Voltage - via Ux pin 64 +/-0.15625 V
Current - via Ix pin 64 +/-0.3125 mA
Voltage - via Ux pin 128 +/-0.078125 V
Current - via Ix pin 128 +/-0.15625 mA

The API delivers measurement values scaled to the nominal input range, i.e. a value of 1.0 represents the maximum of the nominal input range, and a value of -1.0 represents the minimum of the nominal input range.

Behavior on Out-of-Range Input

The ADCs can measure up to the nominal input range * 1.2, i.e. up to +/-12 V for voltage measurement with gain 1, or up to +/-24 mA for current measurement with gain 1.

If the input voltage or current exceeds the maximum input range, the measured value is saturated to the maximum value of the range. For example, if a voltage of +13 V is applied to a channel with gain 1, the measured value is +1.2.

Sensor Supply

Each group has a 24 V/50 mA sensor supply output, which can be used to power external sensors.

Anti-Aliasing Filter

Each channel has a built-in low-pass filter to suppress aliasing effects. The aliasing filter is selected automatically according to the sample rate.

Connection

Each analog group has its own connector with the following pinout:

Pin Symbol Description
1 +24V Supply Voltage Output
2 Ux First voltage input of the group
3 Ix First current input of the group
4 Ux+1 Second voltage input of the group
5 Ix+1 Second current input of the group
6 GND Ground

LED Indicators

Each channel has a LED which can display the following states:

  • Yellow: channel is almost zero (between -0.1 and +0.1 of nominal range)
  • Red: channel overrange (above +1.0 or below -1.0 of nominal range)
  • Green otherwise

API Description

The IOU09 implements the AnalogInTypeB function block, that presents all 8 channels within one function block instance.

The function block offsers:

  • Configuration of sample rate and gain per channel
  • Start/Stop of sampling per channel
  • Data stream with sampled values
  • Reading the last sampled value of all channels (without starting a stream)

Using the io4edge API to access the Analog Input Function

First, install the io4edge client library.

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

First, install the io4edge client library.

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

Connect to the Analog Input Function

To access the Analog Inputs, create a Client and save it to the variable c. Pass as address either a service address or an ip address with port. Example:. Example:

  • As a service address: S101-IOU09-USB-EXT-1-anain
  • As an IP/Port: e.g. 192.168.201.1:10000

We need this client variable for all further access methods.

import (
    "fmt"
    "os"
    "strconv"
    "time"

    log "github.com/sirupsen/logrus"

    "github.com/ci4rail/io4edge-client-go/v2/pkg/protobufcom/common/functionblock"
    anain "github.com/ci4rail/io4edge-client-go/v2/pkg/protobufcom/functionblockclients/analogintypeb"
    fspb "github.com/ci4rail/io4edge_api/analogInTypeB/go/analogInTypeB/v1"
)

func main() {
    const timeout = 0 // use default timeout

    c, err = anain.NewClientFromUniversalAddress(("S101-IOU09-USB-EXT-1-anain", timeout)
    if err != nil {
        log.Fatalf("Failed to create anain client: %v\n", err)
    }
    ...

To access the analog inputs, create a Client and save it to the variable ana_client. Pass as address either a service address or an ip address with port. Examples:

  • As a service address: S101-IOU09-USB-EXT-1-anain
  • As an IP/Port: 192.168.201.1:10000

We need this client variable for all further access methods.

import io4edge_client.analogintypeb as ana
import io4edge_client.functionblock as fb

def main():
    ana_client = ana.Client("S101-IOU09-USB-EXT-1-anain")
    ...

Configuring Channels

To configure a one or more channels, provide a "channel configuration" array to the UploadConfiguration method. Each entry in the array defines the configuration for one channel. The gain can be set individually per channel, but the sample rate must be the same for both channels of a group. If it is not, the latest sample rate for the group is used.

The gain value must exactly match one of the supported gain values.

The sample does not need to exactly match one of the supported sample rates, the closest supported sample rate is used.

Channels that are not included in the configuration array are not changed.

  // configure channel 0 with sample rate 122.0703 Hz and gain 1
  // configure channel 1 with sample rate 122.0703 Hz and gain 2
  err = c.UploadConfiguration([]*fspb.ChannelConfig{
    {
      Channel:    0,
      SampleRate: 122.0703,
      Gain:       1,
    },
    {
      Channel:    1,
      SampleRate: 122.0703,
      Gain:       2,
    },
  })
  if err != nil {
    ...
  }
    # configure channel 0 with sample rate 122.0703 Hz and gain 1
    # configure channel 1 with sample rate 122.0703 Hz and gain 2

    config = ana.Pb.ConfigurationSet()
    config.channelConfig.add(channel=0, sample_rate=122.0703, gain=1)
    config.channelConfig.add(channel=1, sample_rate=122.0703, gain=2)
    ana_client.upload_configuration(config)

Reading the latest Sampled Values

For slow or DC measurements, it may be sufficient to read the latest sampled values without starting a stream.

Still, the channels are sampled continuously with the configured sample rate and gain, and the latest sampled value is always available.

    values, err := c.Values()
    if err != nil {
        ...
    }
    for i, val := range values {
        fmt.Printf("Current value channel %d: %.4f\n", i, val)
    }
    values = ana_client.value()
    ch = 0
    for value in values:
        print(f"Ch{ch}: {value: .3f}")
        ch += 1

Streamed Sampling

In data logger applications, you may want to record the waveforms on the analog inputs.

Start a Stream

Therefore, the API provides functions to start a Stream. You can select which channels include in the stream.

    // start stream
    err = c.StartStream(
        anain.WithFBStreamOption(functionblock.WithBucketSamples(400)),
        anain.WithFBStreamOption(functionblock.WithBufferedSamples(800)),
        anain.WithChannelMask(0x03), // include channel 0 and 1 in stream
    )
    ana_client.start_stream(
        0x03,  # include channel 0 and 1 in stream
        fb.Pb.StreamControlStart(
            bucketSamples=400,
            keepaliveInterval=1000,
            bufferedSamples=800,
            low_latency_mode=False,
        ),
    )
Receive Samples from Stream

Then, Samples are generated with the configured sample rate in the stream, each sample contains one more values.

  • A timestamp of the sample
  • The base channel number of the values (e.g. 0 for channel 0 and 1, 2 for channel 2 and 3, etc.)
  • Measured values

For efficiency, multiple samples are gathered and sent as one Bucket to the host.

To read samples from the stream:

  ...
    for {
        // read next bucket from stream
        sd, err := c.ReadStream(time.Second * 1)

        if err != nil {
            log.Errorf("ReadStreamData failed: %v\n", err)
        } else {
            samples := sd.FSData.GetSamples()
            fmt.Printf("got stream data seq=%d ts=%d\n", sd.Sequence, sd.DeliveryTimestamp)

            for i, sample := range samples {
                fmt.Printf("  #%d: ts=%d ch %d", i, sample.Timestamp, sample.BaseChannel)
                for _, value := range sample.Value {
                    fmt.Printf(" %.4f", value)
                }
                fmt.Printf("\n")
            }
        }
    }
    while True:
        generic_stream_data, stream_data = ana_client.read_stream()
        print(
            f"Received stream data {datetime.datetime.now()} {generic_stream_data.deliveryTimestampUs}, {generic_stream_data.sequence}"
        )
        for sample in stream_data.samples:
            print(
                " #%d: ts=%d ch %d %s"
                % (n, sample.timestamp, sample.baseChannel, sample.value) # note that sample.value is a list
            )
            n += 1

Warning

At the moment, timestamps are expressed in microseconds relative to the start of the IOU09. 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. If you do not specify any parameters, the default values are used.

  • The BucketSamples parameter (default: 25) defines the number of samples per bucket. If the bucket contains BucketSamples, it is sent to the client.
  • The KeepAliveInterval parameter (default: 1000) 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 (default: 50) 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.
    // configure stream to send the bucket at least once a second
    // configure the maximum samples per bucket to 400
    // configure the buffered samples to 800
    err = c.StartStream(
        anain.WithFBStreamOption(functionblock.WithKeepaliveInterval(1000)),
        anain.WithFBStreamOption(functionblock.WithBucketSamples(400)),
        anain.WithFBStreamOption(functionblock.WithBufferedSamples(800)),
        anain.WithChannelMask(0x03), // include channel 0 and 1 in 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.

    ana_client.start_stream(
        0x03,  # include channel 0 and 1 in stream
        fb.Pb.StreamControlStart(
            bucketSamples=400,
            keepaliveInterval=1000,
            bufferedSamples=800,
            low_latency_mode=False,
        ),
    )