> For the complete documentation index, see [llms.txt](https://docs.roboflow.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.roboflow.com/workflows/tutorials/integrate-with-ignition/live-data/write-results-to-ignition.md).

# Write Results to Ignition

Write a Roboflow Workflow's result into an Ignition tag over OPC UA, so a vision result runs an Ignition tag script, alarm, or counter.

In this path a Roboflow Workflow running on an edge device writes its result straight into an Ignition tag over OPC UA. A tag script in Ignition then runs your action (ex: an alarm, a reject, a counter, or a database record).

{% hint style="info" %}
The OPC UA Writer Sink, the OPC UA Reader, and the OPC UA Server are available exclusively for Enterprise customers. [Contact the Roboflow sales team](https://roboflow.com/sales) to learn more.
{% endhint %}

{% hint style="info" %}
These steps and screenshots are from Ignition 8.1 (Designer 8.1.55). Menu names, dialogs, and property names can differ in other versions, including 8.3.
{% endhint %}

## Set Up the Ignition OPC UA Server

In this path the Roboflow device is the OPC UA client and Ignition is the server. Ignition's defaults block that connection in three ways. In the Gateway, go to "Config" > "OPC UA" > "Server Settings" and click "Show advanced properties":

<table data-search="false"><thead><tr><th>Setting</th><th>Default</th><th>Set to</th><th>Why</th></tr></thead><tbody><tr><td>"Bind Addresses"</td><td><code>localhost</code></td><td><code>0.0.0.0</code></td><td>The device cannot reach a server bound to localhost</td></tr><tr><td>"Security Policies"</td><td><code>Basic256Sha256</code></td><td><code>None,Basic256Sha256</code></td><td>The OPC UA Writer Sink connects without a client certificate</td></tr><tr><td>"Expose Tag Providers"</td><td>Off</td><td>On</td><td>Ignition tags are not visible over OPC UA otherwise</td></tr></tbody></table>

<figure><img src="https://1126624165-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoO0Ceoy9R0lHDZATcPyh%2Fuploads%2Fgit-blob-d4eb99bbba88f95838f26b1e964fe879a743d24d%2Fignition-opcua-server-settings.png?alt=media" alt="The Ignition 8.1 OPC UA Server Settings page with Bind Addresses set to 0.0.0.0, Security Policies set to None,Basic256Sha256, Anonymous Access Allowed off, User Source opcua-module, and Expose Tag Providers on"><figcaption><p>The OPC UA server settings for this path.</p></figcaption></figure>

Click "Save Changes". The server keeps its old settings until the OPC UA module restarts: go to "Config" > "System" > "Modules", click "restart" on the "OPC-UA" row, and confirm.

Leave anonymous access off. Ignition's `opcua-module` user source ships with the user `opcuauser` and a documented default password, and its `AuthenticatedUser` role can write tags. Change that password, or add a separate user for Roboflow.

## Example Tags

This page uses two tags to show the pattern. They are an example for illustration: define your own names, folder, and data types to fit your line, and use the same names in the Workflow.

<table data-search="false"><thead><tr><th>Tag</th><th>Data type</th><th>What it means</th></tr></thead><tbody><tr><td><code>Roboflow/Trigger</code></td><td>Boolean</td><td>The device writes <code>true</code> when the Workflow's condition passes (ex: the model finds a person). A tag script in Ignition runs your action, then sets the tag back to <code>false</code>.</td></tr><tr><td><code>Roboflow/TriggerCount</code></td><td>Integer (Int4)</td><td>Counts how many times the action ran. It stands in for your real action (ex: a reject, an alarm, or a database record).</td></tr></tbody></table>

## Create the Tags and the Action

In Designer, pick the `default` provider in the Tag Browser and create a folder named `Roboflow`. Right-click the folder and pick "New Tag" > "Memory Tag". In the Tag Editor, set "Name" to `Trigger` and "Data Type" to "Boolean", then click "OK". Add `TriggerCount` the same way, with "Data Type" set to "Integer".

<figure><img src="https://1126624165-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoO0Ceoy9R0lHDZATcPyh%2Fuploads%2Fgit-blob-fadbfc0c495319954ba514b198edbb4475dd076c%2Fignition-designer-new-memory-tag.png?alt=media" alt="The Ignition 8.1 Tag Editor for a new tag with Name Trigger, Value Source Memory, and Data Type Boolean"><figcaption><p>The Tag Editor for the new <code>Trigger</code> memory tag.</p></figcaption></figure>

The Writer Sink addresses a tag as "Object Name" plus "Variable Name", which is why the example puts the tags in a folder. To write a tag at the provider root instead, set "Object Name" to the tag's full path (ex: `[default]Trigger`) and leave "Variable Name" empty.

Double-click `Trigger` to open the Tag Editor, go to "Scripting" > "Value Changed", and paste this into the body of the script:

```python
if initialChange or not currentValue.value:
    return
system.util.getLogger("Roboflow").info("Roboflow trigger fired")
count = system.tag.readBlocking(["[default]Roboflow/TriggerCount"])[0].value or 0
system.tag.writeAsync(["[default]Roboflow/TriggerCount", "[default]Roboflow/Trigger"], [count + 1, False])
```

<figure><img src="https://1126624165-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoO0Ceoy9R0lHDZATcPyh%2Fuploads%2Fgit-blob-17772b93bfee8a9f18bc3f8e751ae9929ca3ec9d%2Fignition-designer-trigger-script.png?alt=media" alt="The Ignition Tag Editor for the Trigger tag, on Scripting > Value Changed, with a script that logs Roboflow trigger fired, increments TriggerCount, and sets Trigger back to false"><figcaption><p>The Value Changed script on <code>Trigger</code>.</p></figcaption></figure>

Replace the logger and counter lines with your own action (ex: `system.tag.writeAsync` to a PLC tag, `system.db.runPrepUpdate` to record the event, or an alarm). Keep the last write, which sets `Trigger` back to `false`. Without it, the next `true` is not a value change and the script does not run again.

## Build the Workflow

The Workflow runs a model, checks a condition, and writes `true` to `Trigger` when the condition passes. Build it in the Workflow editor:

1. [Create a Workflow](/workflows/build/create-a-workflow.md) and add an "Object Detection Model" block with your model.
2. Add a "Continue If" block after the model, and set the condition that should start the action (ex: the model found at least one `person`).
3. Add an "OPC UA Writer Sink" block after the Continue If. Search for "OPC UA" in "Workflow Blocks" to find it.

<figure><img src="https://1126624165-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoO0Ceoy9R0lHDZATcPyh%2Fuploads%2Fgit-blob-53083c474eb542fb6f0c3b3555447b728e7546ed%2Fignition-workflow-opc-writer-graph.png?alt=media" alt="A Workflow in the Roboflow Workflow editor with an Object Detection Model block named model, a Continue If block, and an OPC UA Writer Sink block named ignition_trigger, with outputs predictions, opc_error_status, and opc_message" width="400"><figcaption><p>The model, the condition, and the Writer Sink.</p></figcaption></figure>

Click the OPC UA Writer Sink and fill in its fields, then click "Show Additional Properties" for the sign-in and lookup settings. These settings matter for Ignition:

<table data-search="false"><thead><tr><th>Field</th><th>Value</th><th>Why</th></tr></thead><tbody><tr><td>"Url"</td><td><code>opc.tcp://&#x3C;ignition-host>:62541</code></td><td>Ignition's OPC UA endpoint</td></tr><tr><td>"Namespace"</td><td><code>urn:inductiveautomation:ignition:opcua:tags</code></td><td>The namespace URI of Ignition's tags. The block looks up its index when it connects.</td></tr><tr><td>"Object Name"</td><td><code>[default]Roboflow</code></td><td>The tag provider and folder</td></tr><tr><td>"Variable Name"</td><td><code>Trigger</code></td><td>The tag name</td></tr><tr><td>"Value"</td><td><code>1</code></td><td>The value to write. The field takes a number, and the block writes <code>1</code> as <code>true</code> to a Boolean tag.</td></tr><tr><td>"Value Type"</td><td>"Boolean"</td><td>Must match the tag's data type</td></tr><tr><td>"Cooldown Seconds"</td><td><code>5</code></td><td>The shortest time between writes, in seconds. <code>0</code> turns the cooldown off.</td></tr><tr><td>"User Name" and "Password"</td><td>Your Ignition OPC UA user (ex: <code>roboflow</code>)</td><td>Ignition refuses anonymous writes</td></tr><tr><td>"Node Lookup Mode"</td><td>"direct"</td><td>Ignition addresses tags by string NodeId, so the block builds <code>ns=&#x3C;index>;s=[default]Roboflow/Trigger</code>. The default, "hierarchical", does not find Ignition tags.</td></tr><tr><td>"Fire And Forget"</td><td>Off</td><td>Write errors then show in the block's <code>error_status</code> and <code>message</code> outputs. When it's on, the block writes in the background and <code>error_status</code> is always <code>false</code>.</td></tr></tbody></table>

<figure><img src="https://1126624165-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoO0Ceoy9R0lHDZATcPyh%2Fuploads%2Fgit-blob-22cf7e9f9d1816d7c7bba9f934b7a8c0cda6c191%2Fignition-workflow-opc-writer-settings.png?alt=media" alt="The OPC UA Writer Sink settings in the Workflow editor with Url opc.tcp://192.168.1.20:62541, the Ignition tags namespace, Object Name [default]Roboflow, Variable Name Trigger, Value 1, Value Type Boolean, Cooldown Seconds 5, and Additional Properties with User Name roboflow, a password, and Node Lookup Mode direct"><figcaption><p>The Writer Sink set up for the example <code>Trigger</code> tag.</p></figcaption></figure>

The user name and password are saved in the Workflow, so give Roboflow an Ignition user that can write only the tags it needs.

To see write errors on the device, add the block's `error_status` and `message` to the Workflow outputs (ex: as `opc_error_status` and `opc_message`). Then click "Save" to publish the Workflow.

To write a value instead of a pulse, connect "Value" to another block's output (ex: a detection count from a Property Definition block) and match "Value Type" to the tag:

<table data-search="false"><thead><tr><th>Ignition tag type</th><th>"Value Type"</th></tr></thead><tbody><tr><td>Boolean</td><td>Boolean</td></tr><tr><td>Integer (Int4)</td><td>Int32</td></tr><tr><td>Double (Float8)</td><td>Double</td></tr><tr><td>String</td><td>String</td></tr></tbody></table>

The block's "Integer" value type is an alias for "Int64", so it does not match an Ignition "Integer" tag, which is Int4. Use "Int32". The block also supports "Float", "Int16", "SByte", "UInt16", "UInt32", and "UInt64".

## Deploy the Workflow to the Device

Run the Workflow as a stream on the device. In Deployment Manager, open the device and click "Add Stream". Pick your Workflow in "Workflow ID", set "Mode" to "Continuous" so the Workflow runs on every frame, and enter your camera's "RTSP URL". Click "Create Stream". Learn more about how to [add a stream](https://docs.roboflow.com/deployment/self-hosted/enterprise/deployment-manager/setting-up/add-a-stream).

<figure><img src="https://1126624165-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoO0Ceoy9R0lHDZATcPyh%2Fuploads%2Fgit-blob-984d374ce6073fa4a5e55d5aa3b143d738ffa06e%2Fignition-dm-add-stream-continuous.png?alt=media" alt="The Deployment Manager Add New Video Stream dialog with Name line-1-camera, the Workflow selected, Workflow Version Latest (auto-update), Mode Continuous, Stream Type RTSP Stream, and an RTSP URL" width="400"><figcaption><p>A continuous stream runs the Workflow on every frame.</p></figcaption></figure>

The OPC UA settings are saved in the Workflow, so the stream needs no other settings.

## Test the Connection

Once the stream shows "Running", the device writes `true` to `Trigger` each time the condition passes, at most once every 5 seconds. Check both ends:

* In Ignition, `TriggerCount` goes up and the gateway log shows `Roboflow trigger fired`.

<figure><img src="https://1126624165-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoO0Ceoy9R0lHDZATcPyh%2Fuploads%2Fgit-blob-3be63b7b4cb1d061c4389780e21dc7b5e8887e4e%2Fignition-designer-roboflow-tags.png?alt=media" alt="The Ignition Designer Tag Browser with the default provider selected and the Roboflow folder expanded, showing Trigger false and TriggerCount 3"><figcaption><p><code>TriggerCount</code> after three writes from the device.</p></figcaption></figure>

* In Deployment Manager, the device logs show `OPC Writer successfully wrote` at info level after each write, and failures as `OPC Writer` warnings or errors. A rising `TriggerCount` is the sure check, because the success line only shows when the device logs at info level.

To test before the line runs, add the Workflow as a second stream with "Mode" set to "Triggered", point its camera at something that passes the condition, and click "Trigger" on the stream in Deployment Manager. `TriggerCount` goes up by one. Learn more about how to [trigger a stream](https://docs.roboflow.com/deployment/self-hosted/enterprise/deployment-manager/making-changes/trigger-a-stream).

## Troubleshooting

<table data-search="false"><thead><tr><th>Symptom</th><th>Cause</th><th>Fix</th></tr></thead><tbody><tr><td>Device logs <code>WRONG NAMESPACE ERROR</code></td><td>The namespace URI does not match the server, or Ignition tags are not exposed over OPC UA</td><td>Check "Namespace" and turn on "Expose Tag Providers"</td></tr><tr><td>Device logs <code>WRONG OBJECT OR PROPERTY ERROR</code></td><td>The tag path is wrong, a root tag has a "Variable Name", or "Node Lookup Mode" is "hierarchical" against Ignition</td><td>Check "Object Name" and "Variable Name", and set "Node Lookup Mode" to "direct"</td></tr><tr><td>Device logs <code>WRONG TYPE ERROR</code></td><td>"Value Type" does not match the tag's data type</td><td>Match the types (ex: "Int32" for an Ignition "Integer" tag)</td></tr><tr><td>Device logs <code>Connection is closed</code> once after Ignition restarts</td><td>The block's pooled OPC UA session went stale, so that one write is lost</td><td>Nothing to fix. The next write reconnects.</td></tr><tr><td><code>TriggerCount</code> stops rising while the device keeps writing</td><td><code>Trigger</code> is stuck at <code>true</code>, so new writes are not value changes</td><td>Set <code>Trigger</code> to <code>false</code>, and keep the reset line in the script</td></tr><tr><td><code>Trigger</code> or <code>Inspect</code> stays <code>true</code> on a trial gateway</td><td>The Ignition trial expired. OPC UA writes still land, but tag scripts stop running.</td><td>Reset the trial, then set the tag back to <code>false</code></td></tr></tbody></table>

## Security

Keep the device and the Ignition gateway on the plant or OT network.

* The OPC UA Writer Sink and the OPC UA Reader connect with security policy `None`, so OPC UA traffic is not signed or encrypted.
* Binding Ignition's OPC UA server to `0.0.0.0` opens port 62541 on every network interface of the Ignition host.
* Give Roboflow its own Ignition OPC UA user, with write access to the `Roboflow` folder only.
