Skip to content

visionify/visionify-plc-template-application

Repository files navigation

Visionify PLC Template Application

Open-source template for integrating the Visionify AI Safety Platform with Programmable Logic Controllers (PLCs).

This application receives real-time safety events from Visionify via webhooks and translates them into PLC commands — enabling automated machine stops, alerts, and safety interlocks driven by AI-powered vision.

License: MIT


Table of Contents


Overview

The Visionify AI Safety Platform uses computer vision to detect safety hazards in real time — people entering restricted zones, PPE violations, forklift proximity risks, spills, and more. When a safety event is detected, Visionify can trigger a hard-stop or alert action by sending a webhook to this template application.

This template application:

  1. Receives webhook events from Visionify over HTTP (POST requests)
  2. Validates the request using HMAC-SHA256 signature verification
  3. Routes the event to the appropriate action (stop, alert, or ignore) based on your configuration
  4. Executes PLC logic — writing coils, registers, tags, or OPC-UA nodes to control your machinery

This is a template. You download it, configure it for your site, customize the PLC communication code for your specific hardware, and run it on your local network alongside your PLC.


Architecture

┌─────────────────────┐         Webhook (HTTP POST)         ┌──────────────────────────┐
│                     │ ──────────────────────────────────►  │                          │
│   Visionify AI      │   Event: hard_stop, ppe_violation,  │   PLC Template App       │
│   Safety Platform   │   zone_breach, forklift_proximity   │   (this application)     │
│                     │                                      │                          │
│  - Camera feeds     │  ◄─────────────────────────────────  │  - Validates signature   │
│  - AI detection     │         200 OK / acknowledgment      │  - Routes event          │
│  - Trigger rules    │                                      │  - Executes PLC logic    │
│  - Zone config      │                                      │                          │
└─────────────────────┘                                      └────────────┬─────────────┘
                                                                          │
                                                             Modbus TCP / EtherNet/IP /
                                                             OPC-UA / Digital Output
                                                                          │
                                                                          ▼
                                                             ┌──────────────────────────┐
                                                             │                          │
                                                             │   PLC / Safety Relay     │
                                                             │                          │
                                                             │  - Machine stop          │
                                                             │  - HMI alert display     │
                                                             │  - Safety interlock      │
                                                             │                          │
                                                             └──────────────────────────┘

How it works:

  1. Visionify detects a safety event (e.g., person enters a restricted zone near a press machine).
  2. Visionify sends a POST request to this application's webhook endpoint with event details.
  3. This application verifies the signature, looks up the event type in its configuration, and determines the action (stop or alert).
  4. The appropriate PLC handler writes the signal to your PLC — setting a stop coil, writing an event code register, or toggling a digital output.
  5. Your PLC's safety routine takes over (machine halt, alarm, interlock hold, etc.).

Supported PLC Protocols

Protocol Handler Compatible Hardware Library
Modbus TCP modbus_tcp Schneider, ABB, Wago, Delta, any Modbus-compatible PLC pymodbus
EtherNet/IP ethernet_ip Allen-Bradley ControlLogix, CompactLogix, Micro800 pycomm3
OPC-UA opcua Siemens S7-1500, Beckhoff TwinCAT, any OPC-UA server asyncua
Digital Output digital_output Raspberry Pi GPIO, USB relay boards, industrial I/O RPi.GPIO

Don't see your protocol? The handler architecture is modular — see Adding a Custom Handler to create your own.


Quick Start

Prerequisites

  • Python 3.9 or later
  • Network connectivity between this application and your PLC
  • A running Visionify instance with webhook configuration access

1. Clone and Install

git clone https://github.com/visionify/visionify-plc-template-application.git
cd visionify-plc-template-application

# Create a virtual environment
python -m venv venv
source venv/bin/activate  # Linux/macOS
# venv\Scripts\activate   # Windows

# Install base dependencies
pip install -r requirements.txt

# Install the PLC library for your protocol (pick one):
pip install pymodbus>=3.6       # Modbus TCP
pip install pycomm3>=1.2        # EtherNet/IP (Allen-Bradley)
pip install asyncua>=1.0        # OPC-UA
pip install RPi.GPIO>=0.7       # Digital Output (Raspberry Pi)

2. Configure

# Create your site-specific config (git-ignored)
cp config.yaml config.local.yaml

Edit config.local.yaml:

webhook:
  port: 5000
  secret: "your-strong-secret-here"    # Must match Visionify webhook settings

plc:
  protocol: "modbus_tcp"               # Choose: modbus_tcp, ethernet_ip, opcua, digital_output
  modbus_tcp:
    host: "192.168.1.100"              # Your PLC's IP address
    port: 502
    stop_coil_address: 0               # Coil to trigger emergency stop
    event_register_address: 100        # Register for event code

3. Run

python app.py

You should see:

[2026-03-21 10:00:00] INFO     ============================================================
[2026-03-21 10:00:00] INFO     Visionify PLC Template Application
[2026-03-21 10:00:00] INFO     ============================================================
[2026-03-21 10:00:00] INFO     PLC Protocol : modbus_tcp
[2026-03-21 10:00:00] INFO     Webhook URL  : http://0.0.0.0:5000/visionify/webhook
[2026-03-21 10:00:00] INFO     Health Check : http://0.0.0.0:5000/health
[2026-03-21 10:00:00] INFO     Reset URL    : http://0.0.0.0:5000/visionify/reset
[2026-03-21 10:00:00] INFO     ============================================================

4. Configure Visionify

In the Visionify web app:

  1. Navigate to Integrations > Webhook Settings
  2. Click Add Webhook
  3. Enter the endpoint URL: http://<this-machine-ip>:5000/visionify/webhook
  4. Enter the same secret token you configured in config.local.yaml
  5. Select the event types to forward (Hard-Stop Trigger, PPE Violation, etc.)
  6. Click Save, then Send Test Event to verify the connection

5. Test Locally

# Send a simulated hard-stop event
python test_webhook.py

# Send a specific event type
python test_webhook.py --event ppe_violation

# Send all sample events
python test_webhook.py --all

# Test the reset endpoint
python test_webhook.py --reset

Configuration Reference

The configuration file (config.yaml / config.local.yaml) has four sections:

Webhook Settings

webhook:
  host: "0.0.0.0"          # Bind address
  port: 5000                # Listen port
  secret: "your-secret"     # HMAC-SHA256 shared secret
  endpoint: "/visionify/webhook"  # Webhook path

PLC Connection

plc:
  protocol: "modbus_tcp"    # Handler to use

  modbus_tcp:               # Modbus TCP settings
    host: "192.168.1.100"
    port: 502
    unit_id: 1
    stop_coil_address: 0
    event_register_address: 100
    timeout: 3

  ethernet_ip:              # EtherNet/IP settings
    host: "192.168.1.101"
    stop_tag: "Safety_EStop"
    event_tag: "Safety_EventCode"

  opcua:                    # OPC-UA settings
    endpoint: "opc.tcp://192.168.1.102:4840"
    stop_node_id: "ns=2;s=Safety.EStop"
    event_node_id: "ns=2;s=Safety.EventCode"
    username: null
    password: null

  digital_output:           # GPIO / Relay settings
    pin: 17
    logic: "active_high"
    pulse_duration_ms: 0    # 0 = latching

Event Mapping

Map Visionify event types to PLC actions. Each event type can trigger a stop, alert, or ignore action, and writes a numeric event_code to the PLC register for HMI identification.

events:
  mapping:
    hard_stop:
      action: "stop"        # "stop", "alert", or "ignore"
      event_code: 1         # Numeric code written to PLC register
      description: "Emergency stop"

    ppe_violation:
      action: "alert"
      event_code: 10
      description: "PPE violation detected"

Logging

logging:
  level: "INFO"             # DEBUG, INFO, WARNING, ERROR
  file: "logs/plc_webhook.log"
  max_size_mb: 10
  backup_count: 5

Webhook Event Payload

Visionify sends JSON payloads to your webhook endpoint. Here is the structure:

{
  "event_id": "evt-abc-123",
  "event_type": "hard_stop",
  "timestamp": "2026-03-21T14:30:00.000Z",
  "confidence": 0.92,
  "camera": {
    "id": "cam-01",
    "name": "Press Area Camera 1"
  },
  "zone": {
    "id": "zone-a",
    "name": "ZONE-A-PRESS-RESTRICTED"
  },
  "trigger": {
    "id": "trig-01",
    "name": "ZONE-A-INTRUSION-STOP"
  },
  "detection": {
    "type": "person",
    "bounding_box": {
      "x": 320,
      "y": 240,
      "width": 80,
      "height": 200
    }
  }
}
Field Type Description
event_id string Unique identifier for this event
event_type string Event category (matches keys in events.mapping)
timestamp string ISO 8601 timestamp of the detection
confidence float AI model confidence score (0.0 - 1.0)
camera object Camera that captured the event
zone object Safety zone where the event occurred
trigger object Trigger rule that fired (null for alert-only events)
detection object Detection details (type, bounding box, etc.)

Event Types

Event Type Description Default Action
hard_stop Emergency stop triggered by safety violation stop
restricted_zone_breach Person detected in restricted zone stop
forklift_proximity Forklift too close to personnel stop
ppe_violation Required PPE not detected alert
person_detected Person detected in monitored area alert
spill_detected Spill or obstruction detected alert

Signature Verification

Every webhook request includes an X-Visionify-Signature header containing an HMAC-SHA256 signature:

X-Visionify-Signature: sha256=<hex-digest>

The signature is computed over the raw request body using the shared secret configured in both Visionify and this application.


PLC Handler Examples

Modbus TCP — Writing a Stop Coil

In plc_handlers/modbus_tcp.py, the execute_stop() method writes to a coil and register:

from pymodbus.client import ModbusTcpClient

client = ModbusTcpClient(host="192.168.1.100", port=502)
client.connect()

# Write event code to holding register (for HMI display)
client.write_register(address=100, value=event_code, slave=1)

# Set emergency stop coil HIGH
client.write_coil(address=0, value=True, slave=1)

client.close()

EtherNet/IP — Writing Allen-Bradley Tags

In plc_handlers/ethernet_ip.py:

from pycomm3 import LogixDriver

with LogixDriver("192.168.1.101") as plc:
    plc.write(("Safety_EventCode", event_code))
    plc.write(("Safety_EStop", True))

OPC-UA — Writing to Siemens/Beckhoff Nodes

In plc_handlers/opcua.py:

from asyncua.sync import Client
from asyncua import ua

client = Client("opc.tcp://192.168.1.102:4840")
client.connect()

event_node = client.get_node("ns=2;s=Safety.EventCode")
event_node.write_value(ua.Variant(event_code, ua.VariantType.Int32))

stop_node = client.get_node("ns=2;s=Safety.EStop")
stop_node.write_value(ua.Variant(True, ua.VariantType.Boolean))

client.disconnect()

Digital Output — GPIO Relay

In plc_handlers/digital_output.py:

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)
GPIO.output(17, GPIO.HIGH)  # Activate stop relay

Adding a Custom Handler

To add support for a new PLC protocol:

  1. Create a new file in plc_handlers/ (e.g., plc_handlers/profinet.py)
  2. Subclass BasePLCHandler and implement the three required methods:
from plc_handlers.base import BasePLCHandler

class ProfinetHandler(BasePLCHandler):

    def __init__(self, config, logger):
        super().__init__(config, logger)
        self.plc_config = config.get("plc", {}).get("profinet", {})
        # Initialize your connection parameters here

    def execute_stop(self, event_code: int, event: dict) -> dict:
        # Send stop signal via your protocol
        return {"success": True, "protocol": "profinet", "action": "stop"}

    def execute_alert(self, event_code: int, event: dict) -> dict:
        # Send alert signal
        return {"success": True, "protocol": "profinet", "action": "alert"}

    def execute_reset(self) -> dict:
        # Clear stop state
        return {"success": True, "protocol": "profinet", "action": "reset"}
  1. Register it in plc_handlers/__init__.py:
from plc_handlers.profinet import ProfinetHandler

HANDLER_MAP = {
    ...
    "profinet": ProfinetHandler,
}
  1. Add configuration settings under plc.profinet in config.yaml.

Testing

Local Simulation

The application ships in simulation mode by default — all PLC communication is logged but not actually sent. This lets you test the full webhook flow without a live PLC connection.

# Terminal 1: Start the application
python app.py

# Terminal 2: Send test events
python test_webhook.py --all

Health Check

curl http://localhost:5000/health

Returns:

{
  "status": "ok",
  "service": "visionify-plc-template",
  "plc_protocol": "modbus_tcp",
  "timestamp": "2026-03-21T14:30:00.000Z"
}

Connecting to a Real PLC

When you're ready to connect to a real PLC:

  1. Install the appropriate PLC library (see Quick Start)
  2. Open the handler file for your protocol (e.g., plc_handlers/modbus_tcp.py)
  3. Uncomment the real PLC communication blocks (clearly marked in the code)
  4. Update config.local.yaml with your PLC's IP address, port, and register/tag mappings
  5. Restart the application

Safety Note: Always test PLC integration with the machine in a safe state (de-energized or in maintenance interlock mode) before going live.


Deployment

Running as a System Service (Linux)

Create a systemd service file at /etc/systemd/system/visionify-plc.service:

[Unit]
Description=Visionify PLC Template Application
After=network.target

[Service]
Type=simple
User=plcuser
WorkingDirectory=/opt/visionify-plc-template-application
ExecStart=/opt/visionify-plc-template-application/venv/bin/python app.py
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Then:

sudo systemctl daemon-reload
sudo systemctl enable visionify-plc
sudo systemctl start visionify-plc

Running with Docker

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Uncomment and install your PLC library:
# RUN pip install pymodbus>=3.6
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
docker build -t visionify-plc .
docker run -d -p 5000:5000 -v $(pwd)/config.local.yaml:/app/config.local.yaml visionify-plc

Network & Security Considerations

  • This application should run on the same local network as your PLC (OT network)
  • Do not expose the webhook endpoint to the public internet without VPN or firewall protection
  • Use a strong, randomly generated secret token: python -c "import secrets; print(secrets.token_hex(32))"
  • Consider running behind a reverse proxy (nginx) with TLS for the webhook endpoint if Visionify is on a separate network segment

Performance Targets

Metric Target Maximum
Detection-to-trigger latency < 150 ms 300 ms
Trigger-to-PLC signal latency < 50 ms 100 ms
End-to-end (detection to machine stop) < 500 ms 800 ms

Troubleshooting

Symptom Likely Cause Resolution
401 Unauthorized on webhook Secret mismatch Ensure webhook.secret in config matches the token in Visionify webhook settings
Connection refused App not running or wrong port Check the app is running and the port matches the URL configured in Visionify
PLC not responding Network issue or wrong IP/port Verify PLC is reachable: ping <plc-ip>. Check firewall rules on the OT network
No mapping for event type in logs Event type not in config Add the event type to events.mapping in config.yaml
High latency (> 300ms) Network congestion or PLC overload Check network path between app and PLC. Reduce PLC polling frequency
[SIMULATION] in logs Real PLC code not enabled Uncomment the PLC communication blocks in your handler file

Log Levels

Set logging.level in config.yaml:

  • DEBUG — Full request/response details, PLC register values
  • INFO — Event received, actions taken, connection status
  • WARNING — Stop actions, signature failures, missing mappings
  • ERROR — Exceptions, connection failures

API Endpoints

Endpoint Method Description
/visionify/webhook POST Receive Visionify safety events
/visionify/reset POST Remotely reset PLC stop state
/health GET Health check and status

Project Structure

visionify-plc-template-application/
├── app.py                          # Main application entry point
├── config.yaml                     # Configuration template
├── requirements.txt                # Python dependencies
├── test_webhook.py                 # Test script for simulating events
├── plc_handlers/
│   ├── __init__.py                 # Handler factory and registry
│   ├── base.py                     # Abstract base class
│   ├── modbus_tcp.py               # Modbus TCP handler
│   ├── ethernet_ip.py              # EtherNet/IP handler (Allen-Bradley)
│   ├── opcua.py                    # OPC-UA handler (Siemens, Beckhoff)
│   └── digital_output.py          # GPIO / relay handler
├── LICENSE                         # MIT License
└── README.md                       # This file

Contributing

Contributions are welcome! If you've integrated Visionify with a PLC type not yet covered, consider adding a handler.

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/profinet-handler
  3. Add your handler following the Adding a Custom Handler guide
  4. Submit a pull request

Please include:

  • A brief description of the PLC hardware/protocol you tested with
  • Any configuration additions needed in config.yaml
  • Example usage in the handler's module docstring

License

This project is licensed under the MIT License — see the LICENSE file for details.


Visionify AI Safety Platform | visionify.ai | Documentation

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages