Custom PLC integration

Connect to a Velocio PLC with Modbus and C#.

Use the legacy Visual Studio project, current Modbus specifications, and practical frame examples to build a custom USB connection to an Ace or Branch PLC.

Legacy Velocio Modbus C# example application

Example scope

Start with a working USB serial connection.

Ace and Branch PLCs expose Modbus communication over their USB connection. The legacy example uses a C# Windows Forms application to open the Velocio virtual COM port and exchange Modbus RTU-style request and response frames.

01

Map PLC tags

Assign Modbus addresses to vBuilder tags before the PC application attempts to read or write them.

02

Own the COM port

Find the VelocioComm port and make sure vBuilder or another application is not already using it.

03

Read and write values

The sample covers coils and 16-bit values, then shows how to extend the pattern to 32-bit data.

04

Validate each frame

Append the Modbus CRC to requests and verify the CRC, unit ID, function, length, and errors in responses.

Video walkthrough

See the original example in action.

The video demonstrates the project workflow. The CRC helper and 32-bit examples documented below were added after the video was produced.

What you need

PLC software, a Windows IDE, and the source project.

  • vBuilder: configure the PLC, station ID, tag addresses, and writable tags.
  • USB connection: connect the PLC and identify its VelocioComm COM number.
  • Visual Studio: open or upgrade the legacy Windows Forms solution.
  • Protocol reference: keep the official Modbus application and serial guides nearby.

Connection setup

Prepare the PLC before opening the C# application.

  1. 1

    Assign the PLC ID and tag addresses in vBuilder.

    Give each tag used by the PC application a Modbus address. Mark tags writable when the application needs to change them. Record the PLC station ID; the preserved example uses station 1.

  2. 2

    Find the VelocioComm port.

    Connect the PLC over USB, open Windows Device Manager, expand Ports (COM & LPT), and note the COM number shown beside VelocioComm.

  3. 3

    Release the port, then open it at 115200 8-N-1.

    Close any vBuilder connection or other application using the same port. The legacy project opens the selected port at 115200 baud, eight data bits, no parity, one stop bit, and no handshaking.

  4. 4

    Send a request and validate the complete response.

    Build a frame from unit ID, function code, address, and payload; append CRC low byte then high byte. On receipt, reject short frames, CRC failures, unexpected unit IDs, and Modbus exception responses.

Address offset:

The wire-level starting address is zero-based. In the preserved example, an address displayed as 1 in vBuilder is sent as offset 0. Confirm this mapping with a bench test before writing live machine values.

Useful C# details

Carry the important protocol mechanics into a modern project.

These concise examples preserve the behavior of the legacy project without reproducing the entire Windows Forms application.

Serial connection

Open the Velocio COM port.

var port = new SerialPort(portName, 115200, Parity.None, 8, StopBits.One);
port.Handshake = Handshake.None;
port.ReadTimeout = 1000;
port.WriteTimeout = 1000;
port.DataReceived += OnDataReceived;
port.Open();

A serial port is exclusive. Handle access-denied and disconnect errors, and always close and dispose the port when the application exits.

32-bit read request

Request two consecutive registers.

byte[] request = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x02 };
byte[] frame = AppendModbusCrc(request);

A 32-bit integer or float occupies two 16-bit registers. Only read contiguous registers as one request when the vBuilder address map is intentionally contiguous.

CRC-16/Modbus

Append low byte, then high byte.

static ushort ModbusCrc(ReadOnlySpan<byte> data)
{
  ushort crc = 0xFFFF;
  foreach (byte value in data)
  {
    crc ^= value;
    for (int bit = 0; bit < 8; bit++)
      crc = (ushort)((crc & 1) != 0 ? (crc >> 1) ^ 0xA001 : crc >> 1);
  }
  return crc;
}

The sender calculates the CRC across the request, appends the least-significant CRC byte first, and verifies received frames before using their data.

Float decoding

Reorder bytes for a little-endian Windows process.

byte[] littleEndian = { response[4], response[3], response[6], response[5] };
float value = BitConverter.ToSingle(littleEndian, 0);

The response places the high byte first inside each Modbus register. The preserved example swaps the bytes in each word before using BitConverter.

Signed 16-bit decoding

Combine the register bytes in network order.

short value = unchecked((short)((response[3] << 8) | response[4]));

The same byte-order rule applies to unsigned 16-bit values. Validate the byte count before indexing the response buffer.

Float write request

Write two registers with function 0x10.

byte[] valueBytes = BitConverter.GetBytes(value);
byte[] request = { stationId, 0x10, 0x00, 0x00, 0x00, 0x02, 0x04, valueBytes[1], valueBytes[0], valueBytes[3], valueBytes[2] };
byte[] frame = AppendModbusCrc(request);

Bench-test word and byte order with a known value. Different devices can represent multi-register data differently even when each 16-bit register follows Modbus byte order.

Receive-thread safety:

SerialPort.DataReceived runs outside the UI thread. Buffer complete frames and marshal UI updates with the application framework’s dispatcher or invocation mechanism. Never assume one receive event contains exactly one complete Modbus frame.

Protocol reference

Function codes listed in the legacy Velocio guide.

The original documentation lists the following functions. Confirm support against the exact PLC model and firmware used in your installation.

0x01Read Coils
0x02Read Discrete Inputs
0x03Read Holding Registers
0x04Read Input Registers
0x05Write Single Coil
0x06Write Single Register
0x0FWrite Multiple Coils
0x10Write Multiple Registers
0x17Read/Write Multiple Registers

Verified resources

Download the project and use current primary references.

The source archive is preserved for historical value. The protocol links point to the Modbus Organization, and the serial API link points to Microsoft documentation.