Omron FINS
The FINS module targets Omron PLCs over FINS/UDP and FINS/TCP. Zeus handles FINS headers, FINS/TCP node-address handshake, SID matching, end-code exceptions, Memory Area Read/Write/Fill, and Multiple Memory Area Read.
Start with a Virtual PLC
var memory = new FinsSlaveMemory();
memory.DataMemoryWords[100] = 253;
memory.CioWords[10] = 0x0001;
await using var app = ZeusHost.Create(builder =>
{
builder.AddAcquisition(TimeSpan.FromMilliseconds(100));
builder.AddVirtualChannel("fins-link", new FinsSlaveResponder(FinsTransport.Udp, memory));
builder.AddOmronFins("plc", "fins-link", FinsTransport.Udp, new FinsOptions
{
SourceNode = 10,
DestinationNode = 1
}, points: map => map
.DmWord("temperature", address: 100, scale: 0.1).Writable("temperature")
.CioBit("running", address: 10, bitOffset: 0).Writable("running"));
});
await app.StartAsync();
var temperature = app.Points.Get<double>("temperature");
await app.Points.WriteAsync("temperature", 18.6);
Run sample: samples/Zeus.Samples.Console.Fins.
Connect to a Real PLC
FINS/UDP commonly uses port 9600:
builder.AddUdpClient("fins-link", options =>
{
options.Host = "192.168.250.1";
options.Port = 9600;
});
builder.AddOmronFins("plc", "fins-link", FinsTransport.Udp, new FinsOptions
{
SourceNode = 10,
DestinationNode = 1
});
FINS/TCP also commonly uses 9600. Zeus performs the node-address handshake by default:
builder.AddTcpClient("fins-link", "192.168.250.1", 9600);
builder.AddOmronFins("plc", "fins-link", FinsTransport.Tcp, new FinsOptions
{
TcpRequestedClientNode = 10
});
If the PLC rejects automatic node allocation, set UseTcpNodeAddressHandshake = false and provide SourceNode / DestinationNode manually.
Read/Write API
var plc = app.Devices.Get<FinsDevice>("plc");
var dm = await plc.ReadDataMemoryWordsAsync(100, 2);
await plc.WriteDataMemoryWordsAsync(110, [10, 20, 30]);
var bits = await plc.ReadCioBitsAsync(10, bitOffset: 0, count: 8);
await plc.WriteCioBitsAsync(10, bitOffset: 0, [true, false, true]);
await plc.FillWordsAsync(FinsMemoryAreaCode.DataMemoryWord, 200, 10, 0x55AA);
Raw FINS commands are also available:
byte[] data = await plc.ExecuteAsync(command: 0x0101, data: requestData);
Memory Areas
| Area | Word code | Bit code | Point helpers |
|---|---|---|---|
| CIO | CioWord | CioBit | .CioWord(...) / .CioBit(...) |
| WR | WorkWord | WorkBit | .WorkWord(...) / .WorkBit(...) |
| HR | HoldingWord | HoldingBit | .HoldingWord(...) / .HoldingBit(...) |
| AR | AuxiliaryWord | AuxiliaryBit | .AuxiliaryWord(...) / .AuxiliaryBit(...) |
| DM | DataMemoryWord | DataMemoryBit | .DmWord(...) / .DmBit(...) |
| TIM/CNT | TimerCounterValue | TimerCounterFlag | .TimerCounterValue(...) / .TimerCounterFlag(...) |
| EM | current / banked EM | current / banked EM bit | .CurrentEmWord(...), .EmWord(...), etc. |
JSON Configuration
{
"channels": [
{
"name": "fins-link",
"type": "udp",
"options": {
"host": "192.168.250.1",
"port": 9600
}
}
],
"devices": [
{
"name": "plc",
"channel": "fins-link",
"type": "omron-fins",
"options": {
"transport": "udp",
"sourceNode": 10,
"destinationNode": 1
},
"points": [
{
"name": "temperature",
"options": {
"area": "dm",
"address": 100,
"dataType": "word",
"scale": 0.1,
"writable": true
}
},
{
"name": "running",
"options": {
"area": "cio",
"address": 10,
"bit": 0,
"dataType": "bit",
"writable": true
}
}
]
}
]
}
Virtual FINS PLC:
{
"name": "fins-link",
"type": "virtual",
"options": {
"responder": "fins",
"transport": "udp"
}
}
Common Issues
| Symptom | Likely cause | Fix |
|---|---|---|
| Timeout | UDP/TCP port, node id, or network number is wrong | Test with FinsSlaveResponder, then check PLC FINS settings |
| TCP first request fails | Node-address handshake rejected | Set TcpRequestedClientNode or disable handshake and set nodes manually |
| End code returned | Memory area, address, or count unsupported | Inspect the FINS end code and PLC manual |
| 32-bit word order is wrong | PLC project uses opposite word order | Change FinsOptions.WordOrder |
Next: Points and Acquisition.