Metadata-Version: 2.5
Name: logoIO
Version: 0.1.1
Summary: A simple library to interact with Siemens LOGO! PLC digital IO over S7 communication
Author-email: Matheus Kraemer <matheus@schmiede.one>
License-File: LICENSE
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.8
Requires-Dist: python-snap7
Description-Content-Type: text/markdown

# logoIO

A small Python library for reading and writing the digital I/O of a
Siemens LOGO! PLC over its S7 communication interface.

It wraps the `python-snap7` `Logo` client's VM reads and writes, so
instead of tracking VM byte/bit offsets by hand you get a small Python
object model: connect to the LOGO, declare the channels you want to
read or drive, and use them by name.

## How it Works

A Siemens LOGO! PLC can act as an S7 server. Once configured this way,
a client can connect to it over Ethernet using the `snap7.logo.Logo`
client and read or write its VM (V memory) directly by bit address,
for example `V0.0`.

See [`109778776_LOGO_-Client_to_S7_-Server_S7-Com_DOC_en.pdf`](../109778776_LOGO_-Client_to_S7_-Server_S7-Com_DOC_en.pdf)
for the full guide on configuring a LOGO! as an S7 server with
LogoComfort.

`logoIO` automates:

1. **Connecting to the LOGO** over S7, given its IP address and its
   client and server TSAPs.
2. **Modelling digital channels** as Python objects you can read
   (inputs), or read, turn on, turn off, or pulse for a duration
   (outputs), with optional inverted logic.

## Requirements

- Python >= 3.8
- [`python-snap7`](https://pypi.org/project/python-snap7/)
- A LOGO! PLC configured as an S7 server (see the PDF linked above),
  reachable over the network
- LogoComfort, to perform that one-time S7 server configuration

## Installation

From the project root:

```bash
pip install .
```

or, for local development:

```bash
pip install -e .
```

## Usage

`LogoTerminal` connects to the LOGO over S7 and hands you a
`LogoDigitalIOController` at `terminal.digitalIOController`. Use it
as a context manager, which connects on `__enter__` and disconnects
on `__exit__`, or call `connect()` / `disconnect()` directly if you
need to manage the session's lifetime yourself, for example across
multiple methods of a longer-lived object.

```python
from time import sleep

from logoIO.logo import LogoTerminal

with LogoTerminal("192.168.0.3") as terminal:
  # Declare the channels you intend to use before reading or driving them.
  terminal.digitalIOController.createInputChannel("I1", 0, 0)
  terminal.digitalIOController.createOutputChannel(
      "Q1", 0, 0, invertLogic=True
  )

  # Read a digital input.
  print(terminal.digitalIOController.readDigitalInput("I1"))

  # Turn a digital output on / off.
  terminal.digitalIOController.setDigitalOutput("Q1", True)
  sleep(0.5)
  terminal.digitalIOController.setDigitalOutput("Q1", False)

  # Pulse an output on, then off, after a delay.
  terminal.digitalIOController.setOnAndOff("Q1", 0.05)
```

Without the context manager:

```python
terminal = LogoTerminal("192.168.0.3")
terminal.connect()

terminal.digitalIOController.setDigitalOutput("Q1", True)

terminal.disconnect()
```

See [`src/logoIO/example.py`](src/logoIO/example.py) for a runnable
example that reads one input and drives one output. After
`pip install`, it is also available as the `logoio-example` console
script.

### Digital Channels

- A channel is a single bit inside the LOGO's VM (V memory), addressed
  by a byte offset and a bit offset within that byte, for example
  `byteOffset=0, bitOffset=0` for VM address `V0.0`.
- `invertLogic=True` flips the electrical sense of a channel — reading
  it, or calling `setDigitalOutput(name, True)`, returns or writes the
  opposite bit value. Use it for inputs or outputs wired active-low.
- Channels must be created with `createInputChannel` or
  `createOutputChannel` before they can be addressed by
  `readDigitalInput` / `setDigitalOutput` / `setOnAndOff` — the
  controller keeps them in a `{name: LogoDigitalChannel}` dict and will
  raise a `KeyError` for an undeclared channel.

### Block Writes

`writeBlock(address, value)` writes a whole VM byte, word, or double
word in a single S7 write, for example
`terminal.digitalIOController.writeBlock("V0", 0b00000001)`. The
`address`'s format picks the block width: `"V0"` for 8 bits, `"VW0"`
for 16 bits, `"VD0"` for 32 bits. It bypasses the named channel
registry and any `invertLogic`, so use it when you need to set
several outputs of the same block atomically, rather than one S7
write per channel.

See [`src/logoIO/example_block_write.py`](src/logoIO/example_block_write.py)
for a runnable example that drives a byte block. After
`pip install`, it is also available as the
`logoio-example-block-write` console script.

## Project Layout

```
src/logoIO/
├── logo.py                    # LogoTerminal: S7 connection context manager
├── logo_digital_controller.py # LogoDigitalIOController: channel registry / high-level API
├── logo_driver.py             # LogoDigitalChannel: reads/writes a single S7 VM bit
├── example.py                 # Runnable example reading an input and driving an output
└── example_block_write.py     # Runnable example driving a byte block in one write
```

## Licence

MIT — see [`LICENSE`](LICENSE).
