GVCP (GigE Vision Control Protocol)

Table of contents

  1. Overview
  2. DISCOVERY_CMD
  3. DISCOVERY_ACK
  4. Hex dump
  5. Python server/client implementation
  6. Reference

Overview

GVCP (GigE Vision Control Protocol) is a UDP-based protocol used to discover and control GigE Vision cameras over Ethernet, typically on port 3956. Developed by the Automated Imaging Association (AIA), it enables tasks like device discovery, IP configuration, and register access in machine vision systems.

DISCOVERY_CMD

struct gvcp_cmd_header{
	uint8 cMsgKeyCode; // 0x42
	uint8 cFlag;       // 0x11 allow broadcast ack;ack required
	uint16 wCmd;       // discovery_cmd=2;FORCEIP_CMD = 4;READREG_CMD=0x80
	uint16 wLen;       // payload length
	uint16 wReqID;     // request id = 1;READREG id=12345
};

DISCOVERY_ACK

struct gvcp_ack_header{
	uint16 wStatus; //success=0;
	uint16 wAck;    //discover_ack=3;forceip_ack=5;READREG_ACK=0x81
	uint16 wLen;
	uint16 wReqID;
};

struct gvcp_ack_payload{
	uint32 dwSpecVer;
	uint32 dwDevMode;
	uint8 Mac[8];         //last 6 byte
	uint32 dwSupIpSet;
	uint32 dwCurIpSet;
	//uint8 unused1[12];
	uint8 CurIP[16];      //last 4 byte
	uint8 SubMask[16];    //last 4 byte
	uint8 Gateway[16];    //last 4 byte
	char szFacName[32];   //first
	char szModelName[32]; //first
	char szDevVer[32];
	char szFacInfo[48];
	char szSerial[16];
	char szUserName[16];
};

Hex dump

Simulated response

GigE Vision Control Protocol
    Acknowledge Header: DISCOVERY_ACK
        Status: GEV_STATUS_SUCCESS (0x0000)
        Acknowledge: DISCOVERY_ACK (0x0003)
        Payload Length: 0x00f8 (248)
        Request ID: 0x0001 (1)
        [Request In: 1]
        DISCOVERY_ACK Payload
            Spec Version: 0x01000100
            Device Mode: 0x00000001
            Device MAC Address: 2b:3c:4d:5e:00:00 (2b:3c:4d:5e:00:00)
            Supported IP Configuration: 0x00000007
            Current IP Configuration: 0x00000001
            Current IP: 192.168.1.100
            Subnet Mask: 255.255.255.0
            Default Gateway: 192.168.1.1
            Manufacturer Name: MyCompany
            Model Name: GVCPDevice
            Device Version: v1.0
            Manufacturer Specific Info: Manufactured by MyCompany Inc.
            Serial Number: SN12345678
            User-defined Name: admin
0000   00 00 00 03 00 f8 00 01 01 00 01 00 00 00 00 01   ................
0010   00 1a 2b 3c 4d 5e 00 00 00 00 00 07 00 00 00 01   ..+<M^..........
0020   00 00 00 00 00 00 00 00 00 00 00 00 c0 a8 01 64   ...............d
0030   00 00 00 00 00 00 00 00 00 00 00 00 ff ff ff 00   ................
0040   00 00 00 00 00 00 00 00 00 00 00 00 c0 a8 01 01   ................
0050   4d 79 43 6f 6d 70 61 6e 79 00 00 00 00 00 00 00   MyCompany.......
0060   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0070   47 56 43 50 44 65 76 69 63 65 00 00 00 00 00 00   GVCPDevice......
0080   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0090   76 31 2e 30 00 00 00 00 00 00 00 00 00 00 00 00   v1.0............
00a0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00b0   4d 61 6e 75 66 61 63 74 75 72 65 64 20 62 79 20   Manufactured by 
00c0   4d 79 43 6f 6d 70 61 6e 79 20 49 6e 63 2e 00 00   MyCompany Inc...
00d0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00e0   53 4e 31 32 33 34 35 36 37 38 00 00 00 00 00 00   SN12345678......
00f0   61 64 6d 69 6e 00 00 00 00 00 00 00 00 00 00 00   admin...........

Real response

GigE Vision Control Protocol
    Acknowledge Header: DISCOVERY_ACK
        Status: GEV_STATUS_SUCCESS (0x0000)
        Acknowledge: DISCOVERY_ACK (0x0003)
        Payload Length: 0x00f8 (248)
        Request ID: 0x0001 (1)
        DISCOVERY_ACK Payload
            Spec Version: 0x00020001
            Device Mode: 0x80000001
            Device MAC Address: Keyence_XX:XX:XX (XX:XX:XX:XX:XX:XX)
            Supported IP Configuration: 0x00000007
            Current IP Configuration: 0x00000007
            Current IP: 10.9.2.29
            Subnet Mask: 255.255.255.0
            Default Gateway: 10.9.2.254
            Manufacturer Name: KEYENCE CORPORATION
            Model Name: VS-L500MX
            Device Version: 1.5.0004
            Manufacturer Specific Info: 
            Serial Number: XXXXXXXXXX
            User-defined Name: M202OP110_Split
0000   00 00 00 03 00 f8 00 01 00 02 00 01 80 00 00 01   ................
0010   00 00 XX XX XX XX XX XX 00 00 00 07 00 00 00 07   ......r.........
0020   00 00 00 00 00 00 00 00 00 00 00 00 0a 09 02 1d   ................
0030   00 00 00 00 00 00 00 00 00 00 00 00 ff ff ff 00   ................
0040   00 00 00 00 00 00 00 00 00 00 00 00 0a 09 02 fe   ................
0050   4b 45 59 45 4e 43 45 20 43 4f 52 50 4f 52 41 54   KEYENCE CORPORAT
0060   49 4f 4e 00 00 00 00 00 00 00 00 00 00 00 00 00   ION.............
0070   56 53 2d 4c 35 30 30 4d 58 00 00 00 00 00 00 00   VS-L500MX.......
0080   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0090   31 2e 35 2e 30 30 30 34 00 00 00 00 00 00 00 00   1.5.0004........
00a0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00b0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00c0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00d0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00e0   XX XX XX XX XX XX XX XX XX XX 00 00 00 00 00 00   XXXXXXXXXX......
00f0   4d 32 30 32 4f 50 31 31 30 5f 53 70 6c 69 74 00   M202OP110_Split.

Python server/client implementation

python3 gvcp_client_server.py --server
python3 gvcp_client_server.py --client --ip 127.0.0.1
import socket
import struct
import argparse

GVCP_DISCOVERY_CMD = 0x0002
GVCP_DISCOVERY_ACK = 0x0003

def build_gvcp_ack(req_id):
    # Header
    wStatus = 0
    wAck = GVCP_DISCOVERY_ACK
    wLen = 0  # will be calculated after payload
    wReqID = req_id

    # Payload mock values
    dwSpecVer = 0x01000100
    dwDevMode = 0x00000001
    mac = bytes([0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E]) + bytes([0, 0])  # 8 bytes
    dwSupIpSet = 0x00000007
    dwCurIpSet = 0x00000001

    def padded_ip(ip):
        b = socket.inet_aton(ip)
        return b.rjust(16, b'\x00')

    CurIP = padded_ip("192.168.1.100")
    SubMask = padded_ip("255.255.255.0")
    Gateway = padded_ip("192.168.1.1")

    def padded_str(s, size):
        return s.encode('ascii').ljust(size, b'\x00')

    szFacName = padded_str("MyCompany", 32)
    szModelName = padded_str("GVCPDevice", 32)
    szDevVer = padded_str("v1.0", 32)
    szFacInfo = padded_str("Manufactured by MyCompany Inc.", 48)
    szSerial = padded_str("SN12345678", 16)
    szUserName = padded_str("admin", 16)

    payload = struct.pack(
        "!II8sII16s16s16s32s32s32s48s16s16s",
        dwSpecVer,
        dwDevMode,
        mac,
        dwSupIpSet,
        dwCurIpSet,
        CurIP,
        SubMask,
        Gateway,
        szFacName,
        szModelName,
        szDevVer,
        szFacInfo,
        szSerial,
        szUserName
    )

    wLen = len(payload)
    header = struct.pack("!HHHH", wStatus, wAck, wLen, wReqID)
    return header + payload

def run_server(listen_port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind(("", listen_port))
    print(f"Server listening on UDP port {listen_port}")

    while True:
        data, addr = sock.recvfrom(1024)
        print(f"\nReceived {len(data)} bytes from {addr}")
        print("Raw data:", data.hex())

        if len(data) >= 8:
            cMsgKeyCode, cFlag, wCmd, wLen, wReqID = struct.unpack("!BBHHH", data[:8])
            print("GVCP Header:")
            print(f"  MsgKeyCode: 0x{cMsgKeyCode:02X}")
            print(f"  Flag:       0x{cFlag:02X}")
            print(f"  Cmd:        0x{wCmd:04X}")
            print(f"  Length:     {wLen}")
            print(f"  ReqID:      {wReqID}")

            if wCmd == GVCP_DISCOVERY_CMD:
                print(">> Discovery command received. Sending ACK.")
                response = build_gvcp_ack(wReqID)
                sock.sendto(response, addr)
                print(">> ACK sent.\n")

def send_gvcp_discovery(target_ip, target_port):
    cMsgKeyCode = 0x42
    cFlag = 0x11
    wCmd = GVCP_DISCOVERY_CMD
    wLen = 0
    wReqID = 1
    packet = struct.pack("!BBHHH", cMsgKeyCode, cFlag, wCmd, wLen, wReqID)

    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    sock.settimeout(2.0)

    sock.sendto(packet, (target_ip, target_port))
    print(f"Sent GVCP Discovery to {target_ip}:{target_port}")

    try:
        data, addr = sock.recvfrom(2048)
        print(f"\nReceived response from {addr}")
        print(data.hex())
    except socket.timeout:
        print("No response received (timeout)")

    sock.close()

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    mode = parser.add_mutually_exclusive_group(required=True)
    mode.add_argument("--client", action="store_true", help="Run as GVCP client")
    mode.add_argument("--server", action="store_true", help="Run as GVCP server")
    parser.add_argument("--ip", help="Target IP address (required for client)")
    parser.add_argument("--port", type=int, default=3956, help="Port to listen or send")

    args = parser.parse_args()

    if args.client:
        if not args.ip:
            print("Error: --ip is required in client mode")
        else:
            send_gvcp_discovery(args.ip, args.port)
    elif args.server:
        run_server(args.port)

Reference

GigE Vision Specification
https://github.com/BigSensor/GvcpServer/tree/main
GVCP Wireshark Dissector