IOU01 Detailed Description
Binary I/Os
Features
The IOU01 has a binary I/O function block with 4 channels, corresponding to 4 I/O pins.
- 2 galvanically isolated groups, group1=I/O 1/2, group2=I/O 3/4
- Each pin can be used as a
- binary output with read-back
- binary input
- Each group may switch the load to ground or to supply
- Pins are overcurrent protected
- The input circuit of each pin draws 0.5mA, and supports a configurable fritting function, that draws 10mA for a short moment.
- Supply voltage of each group may be between 24VDC and 110VDC nominal.
- Switching capability of each pin is at least 1A.
- Each pin has a LED that reflects the input state. In case of overcurrent, all 4 LEDs are flashing fast.
- Configurable output watchdog, to reset outputs in case host application crashed
- Max. Input Frequency: 50Hz
- Max. Output Frequency: 50Hz
- Max. Inductive Load: 20 mH
Connection
Each binary I/O group has its own connector:
Pin | Symbol | Description |
---|---|---|
1 | CI | Common Reference voltage for inputs |
2 | IO1/3 | First I/O pin of the group |
3 | IO2/4 | Second I/O pin of the group |
4 | CO | Common Voltage connected to the output switches |
Mating connectors for IOU01:
- Wuerth Electronic P/N 691381030004
Use Cases
How to Connect the Load
Load may be connected to ground (high side switch) or to supply (low side switch).
Warning Do not leave the CI pin open! If the CI pin is left open, one I/O may affect the read-back level of the other I/O!
Using Inputs
In case you want to use a pin as input, you can select whether the input needs to be driven to high or low. The switching threshold of the input is ~10V with a hysteresis of ~200mV.
Controlling a DC Motor
This example shows how you can control a DC Motor in both directions:
Warning When changing direction, open the currently closed contacts first, ensure they are open before closing the complementary switch (brake before make)!
Mixed Application
This example shows how you can use one pin of a group as input and the other pin as output. It also shows that you can supply each group with a different voltage:
Using the io4edge API to access the Binary I/Os
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 binary I/O function
To access the binary I/Os, create a Client and save it to the variable c
. Pass as address either a service address or an ip address with port. Examples:
- As a service address:
S101-IOU01-USB-EXT-1-binaryIoTypeA
- As an IP/Port:
192.168.201.1:10002
We need this client variable for all further access methods.
import (
"time"
log "github.com/sirupsen/logrus"
binio "github.com/ci4rail/io4edge-client-go/binaryiotypea"
)
func main() {
c, err := binio.NewClientFromUniversalAddress(address, time.Second)
if err != nil {
log.Fatalf("Failed to create binio client: %v\n", err)
}
}
To access the binary I/Os, create a Client and save it to the variable binio_client
. Pass as address either a service address or an ip address with port. Examples:
- As a service address:
S101-IOU01-USB-EXT-1-binaryIoTypeA
- As an IP/Port:
192.168.201.1:10002
We need this client variable for all further access methods.
import io4edge_client.binaryiotypea as binio
import io4edge_client.functionblock as fb
def main():
binio_client = binio.Client(address)
Configure the binary I/Os
The following attributes can be configured:
- Output watchdog
- Input fritting
You don’t need to configure the direction of the channels: As long as you don’t activate the output switch, the channel is an input.
Output Watchdog
By default, the outputs keep their commanded state forever, even if the host program has terminated or crashed.
To ensure that outputs are turned off in such cases, the firmware implements a watchdog functionality. The watchdog can be enabled per pin and the watchdog timeout is configurable (but is the same for all pins).
When you enable the watchdog and set the timeout to 2000, the host must periodically set the switch to “on” within 2 seconds. If it fails to do so, the firmware turns off the output.
Fritting
The current drawn by the input circuitry is 0.5mA. To avoid contact corrosion, it is advisable to draw a higher current from time to time. Therefore, the IOU01 supports an optional fritting pulse that applies a 10mA pulse for 10ms each second.
Apply the configuration
The go API allows to configure each attribute individually, for example:
Set 2 seconds watchdog on output0, but not on other outputs:
if err := c.UploadConfiguration(binio.WithOutputWatchdog(0x1, 2000)); err != nil {
log.Fatalf("Failed to set configuration: %v\n", err)
}
Enable fritting for all inputs:
if err := c.UploadConfiguration(binio.WithInputFritting(0xf); err != nil {
log.Fatalf("Failed to set configuration: %v\n", err)
}
Enable fritting on channel #1, #2, #3 and #4, enable watchdog on channel #1 and #2 and set the watchdog timeout to 2 seconds:
config = binio.Pb.ConfigurationSet(outputFrittingMask=0xf,
outputWatchdogMask=0x3,
outputWatchdogTimeout=2000)
Controlling Output Switches
The API provides two methods to control output switches
- Control a single pin
- Control multiple pins
Control a single pin:
// switch on binary output #1
err = c.SetOutput(0, true)
Control multiple pins using a bit mask. The second parameter to SetAllOutputs
is a mask that specifies which outputs are affected:
// switch on binary output #1, turn off output #2, don't change output #3 and #4
err := c.SetAllOutputs(0x1, 0x3)
// switch off all outputs
err := c.SetAllOutputs(0x0, 0xf)
Control a single pin:
# switch on binary output #1
binio_client.set_output(0, True)
Control multiple pins using a bit mask. The second parameter to set_all_outputs
is a mask that specifies which outputs are affected:
# switch on binary output #1, turn off output #2, don't change output #3 and #4
binio_client.set_all_outputs(0x1, 0x3)
# switch off all outputs
binio_client.set_all_outputs(0x0, 0xf)
Overcurrent Protection
The outputs are overcurrent protected. In case an overcurrent condition is detected on one output, ALL outputs are disabled. The firmware attempts after some milliseconds to turn on the previously closed switches again, because the overcurrent may come from a capacitive load or from a temporary short circuit.
If after approx. 50ms the overcurrent condition still remains, ALL outputs enter an error state.
- In the error state, no outputs can be set; inputs can still be read.
- During error state, all 4 LEDs are flashing fast
Application may clear the error state (in case application knows that the root cause of the error vanished):
err := c.ExitErrorState()
binio_client.exit_error_state()
This tells the binary output controller to try again. It does however not wait if the recovery was successful or not.
Reading Inputs
The API provides two methods to read the current state of the pins:
- Get value of a single pin
- Get value of multiple pins
// read state of I/O #1
// value will be true, if the level is above the input switching threshold
value, err := c.Input(0)
// read state of I/O #1 and #2.
// values contains then a bit mask with the state of each input
values, err = c.AllInputs(0x3)
# read state of I/O #1
# value will be true, if the level is above the input switching threshold
value = binio_client.input(0)
# read state of I/O #1 and #2.
# values contains then a bit mask with the state of each input
values = binio_client.all_inputs(0x3)
Input Transient Recording
In data logger applications, you may want to record changes of the inputs.
Therefore, the API provides functions to start a Stream. At stream creation, you select the pins which you want to monitor for changes.
The IOU01 samples the channel values at a rate of 100Hz, so the timestamps are accurate to 10ms.
// start stream, watch for changes on I/O #1 and 2
err = c.StartStream(binio.WithChannelFilterMask(0x3))
// alternatively, use the defaults, i.e. watch for changes on all 4 pins
err = c.StartStream()
For each transition, a Sample is generated in the stream, each sample contains:
- A timestamp of the transition
- The pin that changed
- New value of the pin
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 channel=%d %t\n", i, sample.Timestamp, sample.Channel, sample.Value)
}
}
}
# start stream, watch for changes on I/O #1 and 2
binio_client.start_stream(
binio.Pb.StreamControlStart(channelFilterMask=0x3),
fb.Pb.StreamControlStart(
bucketSamples=25,
keepaliveInterval=1000,
bufferedSamples=50,
low_latency_mode=True,
),
)
For each transition, a Sample is generated in the stream, each sample contains:
- A timestamp of the transition
- The pin that changed
- New value of the pin
For efficiency, multiple samples are gathered and sent as one Bucket to the host. To read samples from the stream:
while True:
generic_stream_data, stream_data = binio_client.read_stream()
print(
f"Received stream data {generic_stream_data.deliveryTimestampUs}, {generic_stream_data.sequence}"
)
for sample in stream_data.samples:
print(" Channel %d -> %d" % (sample.channel, sample.value))
NOTE: At the moment, timestamps are expressed in micro seconds relative to the start of the IOU01. 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 containsBucketSamples
, 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 theBucketSamples
. Select a higher number if your reception process is slow to avoid buffer overruns. -
If you want low latency on the received data, you can enable the “low latency” mode by using
LowLatencyMode
(default:false
). In this mode, samples are sent as soon as possible after they have been received. This means that the buckets contain1..BufferedSamples
samples.
// configure stream to send the bucket at least once a second
// configure the maximum samples per bucket to 25
// configure low latency mode
// configure the buffered samples to 200
err = c.StartStream(
binio.WithFBStreamOption(functionblock.WithKeepaliveInterval(1000)),
binio.WithFBStreamOption(functionblock.WithBucketSamples(25)),
binio.WithFBStreamOption(functionblock.WithLowLatencyMode(true))
binio.WithFBStreamOption(functionblock.WithBufferedSamples(200)),
)
The stream behavior can be fine-tuned to the application needs:
-
The
bucketSamples
parameter defines the number of samples per bucket. If the bucket containsbucketSamples
, 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 thebucketSamples
. Select a higher number if your reception process is slow to avoid buffer overruns. -
If you want low latency on the received data, you can enable the “low latency” mode by setting
low_latency_mode
toTrue
. In this mode, samples are sent as soon as possible after they have been received. This means that the buckets contain1..bufferedSamples
samples.
# start stream, watch for changes on I/O #1 and 2
binio_client.start_stream(
binio.Pb.StreamControlStart(channelFilterMask=0x3),
fb.Pb.StreamControlStart(
bucketSamples=25,
keepaliveInterval=1000,
bufferedSamples=50,
low_latency_mode=True,
),
)
Multiple Clients
It is possible to have multiple clients. Example usage:
- Each client has its own stream. One client may have a stream that records transitions on I/O #1, while another client records transitions on I/O #2, #3 and #4.
- One client is reading the current state of the I/Os, while another client is recording transitions and a third client is writing to an I/O.
Analog Inputs
The IOU01 has two analog in function blocks each with one analog input.
Features
- Delta-Sigma Converter with 300Hz to 4000Hz sampling frequency
- up to 1500Hz: with guaranteed timing jitter less than 150µs
- above 1500Hz: possible higher jitter and sporadic drop of samples
- Resolution depends on the sampling frequency
- 15 bit @300Hz
- 13 bit @1000Hz
- 11.5 bit @2000Hz
- 8.8 bit @4000Hz
- Accuracy: Better than 0.5% to full scale @300Hz sampling rate
- Voltage or current measurement
- Voltage measurement range +/-10V. Input impedance 100kOhms/1nF.
- Current measurement range +/-20mA. Impedance 50Ohms.
- Each analog input is completely isolated
- Integrated power supply for sensor: 24V, up to 24mA
Connection
Each analog I/O group has its own connector:
Pin | Symbol | Description |
---|---|---|
1 | +24V | Sensor supply output |
2 | Uin | Voltage Input |
3 | Iin | Current Input |
4 | 0V | Measurement Ground |
Mating connectors for IOU01:
- Wuerth Electronic P/N 691381030004
Aliasing Considerations
Note that the IOU01 analog inputs do not have anti-aliasing filters. This means that the input signal must be sampled at a rate that is at least twice the highest frequency component of the signal. For example, if the input signal has a 1kHz sine wave, the sampling rate must be at least 2kHz. If the sampling rate is too low, the input signal will be aliased, and the output will be distorted.
Or in other words, your signal must not have frequency components higher than half of the sampling frequency.
Using the io4edge API to access the Analog Inputs
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:
- As a service address:
S101-IOU01-USB-EXT-1-analogInTypeA1
- As an IP/Port:
192.168.201.1:10000
We need this client variable for all further access methods.
import (
time
log "github.com/sirupsen/logrus"
anain "github.com/ci4rail/io4edge-client-go/analogintypea"
"github.com/ci4rail/io4edge-client-go/functionblock"
)
func main() {
c, err := anain.NewClientFromUniversalAddress("S101-IUO01-USB-EXT-1-analogInTypeA1", time.Second)
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-IOU01-USB-EXT-1-analogInTypeA1
- As an IP/Port:
192.168.201.1:10000
We need this client variable for all further access methods.
import io4edge_client.analogintypea as ana
import io4edge_client.functionblock as fb
def main():
ana_client = ana.Client(args.addr)
Set the Sampling Rate
The sample rate is by default set to 300Hz. To change it to a higher value, use
// set sampleRate to 1000 Hz
if err := c.UploadConfiguration(anain.WithSampleRate(uint32(1000))); err != nil {
log.Fatalf("Failed to set configuration: %v\n", err)
}
# configure the function block
config = ana.Pb.ConfigurationSet(sample_rate=1000)
ana_client.upload_configuration(config)
This setting remains active until you change it again or restart the device.
Reading Input Value
To read the current value of the analog input:
val, err := c.Value()
val = ana_client.value()
Where val
is a floating point number reflecting the current value, from -1.0..+1.0, corresponding to the full scale.
For voltage measurement, a value of -1.0
corresponds to -10V, and +1.0
corresponds to +10V.
For current measurement, a value of -1.0
corresponds to -20mA, and +1.0
corresponds to +20mA.
Note that the value is updated internally with the configured sample rate. If high accuracy is required, set the sample rate to the lowest sample rate (300Hz).
Streamed Sampling
In data logger applications, you may want to record the waveform on the analog input.
Therefore, the API provides functions to start a Stream.
// start stream
err = c.StartStream()
# start stream
ana_client.start_stream(
fb.Pb.StreamControlStart(
bucketSamples=100,
keepaliveInterval=1000,
bufferedSamples=200,
low_latency_mode=False,
),
)
Then, Samples are generated with the configured sample rate in the stream, each sample contains:
- A timestamp of the sample
- Measured value
For efficiency, multiple samples are gathered are 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 %.4f\n", i, sample.Timestamp, sample.Value)
}
}
}
n = 0
while True:
generic_stream_data, stream_data = ana_client.read_stream()
print(
f"Received stream data {generic_stream_data.deliveryTimestampUs}, {generic_stream_data.sequence}"
)
for sample in stream_data.samples:
print(" #%d: ts=%d %.4f" % (n, sample.timestamp, sample.value))
n += 1
sample.Value
is as described in the section above
NOTE: At the moment, timestamps are expressed in micro seconds relative to the start of the IOU01. 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 containsBucketSamples
, 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 theBucketSamples
. 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 samples per bucket to 100
// configure the buffered samples to 200
err = c.StartStream(
functionblock.WithKeepAliveInterval(1000),
functionblock.WithBucketSamples(100),
functionblock.WithBufferedSamples(200),
)
The stream behavior can be fine-tuned to the application needs:
-
The
bucketSamples
parameter defines the number of samples per bucket. If the bucket containsbucketSamples
, 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 thebucketSamples
. Select a higher number if your reception process is slow to avoid buffer overruns.
# start stream
ana_client.start_stream(
fb.Pb.StreamControlStart(
bucketSamples=100,
keepaliveInterval=1000,
bufferedSamples=200,
low_latency_mode=False,
),
)
Multiple Clients
It is possible to have multiple clients active at the same time. For example: One client reads the current value of the analog channel, another client reads the stream
Note that all clients use the same sampling rate on a particular analog channel.