Quick Start: Python#

The minimum working program is three lines:

import synergy_logic

base = synergy_logic.Base()
base.print_modules()
$ python3 modules.py
Slot 1 - SLM-DCI-16NP
Slot 2 - SLM-DCO-15P
Slot 3 - SLM-AI-8-V

If this lists the modules on the rail correctly, the library is working.

Printing base modules#

Calling the Base constructor initializes the base controller and begins the module sign-on process. Every configured module responds and is added to the base’s object tree.

print_modules() prints each slot and its module name. See the API reference for the constructor’s optional arguments (including zero-based indexing).

The Base Class#

The Base object exposes the backplane as a two-level index:

module = base[1]       # IO_Module in slot 1
channel = base[1][2]   # channel 2 of slot 1

Slots and channels are numbered from 1 by default, matching the editor’s I/O Mapping table. See the API reference for the complete class.

Read a channel#

sensor = base[1][1]
print(sensor.value)

.value returns the value of the channel. For analog channels, .real returns the same reading converted to engineering units:

analog = base[3][1]
print(f"{analog.value} counts = {analog.real:.2f} V")

Write a channel#

output = base[2][1]
output.value = True       # discrete
output.real = 5.0         # analog (volts, milliamps, °C — depends on the module)

Combo modules#

Combo modules carry both inputs and outputs. Plain [] indexing is ambiguous, so inputs and outputs are addressed through separate properties:

combo = base[1]
sensor = combo.inputs[1]
relay  = combo.outputs[1]

See the API reference for the full property list.

IO_Module class object#

Whole-module operations and configuration go through the module object:

module = base[2]

module.values                      # list of every channel's value
module.do_bitmapped(0b00001111)    # write all outputs in one SPI transaction
mask = module.di_bitmapped()       # read all inputs as one integer

Bitmapped calls use a single bus transaction for the whole module and are substantially faster than iterating over channels when driving many at once.

To configure a module:

module = base[3]                       # SLM-AI-8-V
module.configure_module((0x40, 0x03))  # enable channels 1-4 only

Configuration byte values and the list of configurable modules are in the API reference.

Verify modules sign-on#

rollCall sends an array of expected modules and compares them against those signed on. Raises ValueError on a mismatch, naming the expected module, the module found, and its position in the list.

base.rollCall([
    "SLM-DCI-16NP",
    "SLM-DCO-15P",
    "SLM-AI-8-V",
])

Call this at the start of any program that assumes a particular rail.

Example program#

A complete program that reads an analog input, drives an output proportionally, and shuts down cleanly on exit.

Clean shutdown uses the standard signal library to catch SIGINT (Ctrl-C from a terminal) and SIGTERM (sent by a service manager stopping the program). Both signals set a flag that breaks the main loop; the finally block then closes the output before the program exits.

For manual testing, the toggle switch can be read in the same way to trigger a simulated failure or stop. See Reading the toggle switch for the switch-reading pattern.

#!/usr/bin/env python3
"""Proportional valve control from a level transmitter."""

import signal
import time

import synergy_logic

base = synergy_logic.Base()
base.rollCall(["SLM-AI-8-V", "SLM-AO-8-V"])

level = base[1][1]
valve = base[2][1]

running = True

def shutdown(signum, frame):
    global running
    running = False

signal.signal(signal.SIGTERM, shutdown)
signal.signal(signal.SIGINT, shutdown)

try:
    while running:
        volts = level.real
        valve.real = min(max(volts, 0.0), 10.0)
        time.sleep(0.1)
finally:
    valve.real = 0.0
    print("stopped, valve closed")

The finally block is the important part

Without it, killing the program leaves the valve in whatever state it was last commanded. Nothing in Linux or in the library closes it automatically.

Every program that drives an output needs a shutdown path that leaves the plant in a safe state, and that path must run on SIGTERM as well as Ctrl-C.

Next#