C · recipe

Subscribe to alarms and events in C

Create an event monitored item on the Server object with an explicit select clause. In C you get the field values back as a positional array, so keep the select clause and the unpacking code next to each other.

Stackopen62541
Version1.5.8

The code

#include <open62541/client_config_default.h>
#include <open62541/client_highlevel.h>
#include <open62541/client_subscriptions.h>
#include <stdio.h>

#define NFIELDS 4

static void onEvent(UA_Client *client, UA_UInt32 subId, void *subContext,
                    UA_UInt32 monId, void *monContext,
                    size_t nEventFields, UA_Variant *eventFields) {
    (void)client; (void)subId; (void)subContext; (void)monId; (void)monContext;
    /* order matches the select clause below: Severity, SourceName, Message, Time */
    if (nEventFields < NFIELDS) return;
    UA_UInt16 sev = 0;
    if (eventFields[0].type == &UA_TYPES[UA_TYPES_UINT16])
        sev = *(UA_UInt16 *)eventFields[0].data;
    UA_String src = UA_STRING_NULL;
    if (eventFields[1].type == &UA_TYPES[UA_TYPES_STRING])
        src = *(UA_String *)eventFields[1].data;
    printf("[%u] %.*s\n", sev, (int)src.length, src.data);
}

static UA_SimpleAttributeOperand *makeSelectClauses(void) {
    UA_SimpleAttributeOperand *sc =
        (UA_SimpleAttributeOperand *)UA_Array_new(
            NFIELDS, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]);
    char *names[NFIELDS] = { "Severity", "SourceName", "Message", "Time" };
    for (size_t i = 0; i < NFIELDS; i++) {
        UA_SimpleAttributeOperand_init(&sc[i]);
        sc[i].typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
        sc[i].attributeId = UA_ATTRIBUTEID_VALUE;
        sc[i].browsePathSize = 1;
        sc[i].browsePath = UA_QualifiedName_new();
        *sc[i].browsePath = UA_QUALIFIEDNAME_ALLOC(0, names[i]);
    }
    return sc;
}

int main(void) {
    UA_Client *client = UA_Client_new();
    UA_ClientConfig_setDefault(UA_Client_getConfig(client));
    if (UA_Client_connect(client, "opc.tcp://milo.digitalpetri.com:62541/milo") != UA_STATUSCODE_GOOD) {
        UA_Client_delete(client); return 1;
    }

    UA_CreateSubscriptionRequest sreq = UA_CreateSubscriptionRequest_default();
    UA_CreateSubscriptionResponse sres =
        UA_Client_Subscriptions_create(client, sreq, NULL, NULL, NULL);

    UA_MonitoredItemCreateRequest mreq;
    UA_MonitoredItemCreateRequest_init(&mreq);
    mreq.itemToMonitor.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
    mreq.itemToMonitor.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER;
    mreq.monitoringMode = UA_MONITORINGMODE_REPORTING;

    UA_EventFilter filter;
    UA_EventFilter_init(&filter);
    filter.selectClauses = makeSelectClauses();
    filter.selectClausesSize = NFIELDS;
    UA_ExtensionObject_setValue(&mreq.requestedParameters.filter, &filter,
                                &UA_TYPES[UA_TYPES_EVENTFILTER]);
    mreq.requestedParameters.queueSize = 100;

    UA_Client_MonitoredItems_createEvent(
        client, sres.subscriptionId, UA_TIMESTAMPSTORETURN_BOTH,
        mreq, NULL, onEvent, NULL);

    for (int i = 0; i < 100; i++)
        UA_Client_run_iterate(client, 100);

    UA_Client_disconnect(client);
    UA_Client_delete(client);
    return 0;
}

What it prints

[500] MachineTool_1
[800] MachineTool_1
[100] Server

Notes

  • The select clause order is the field order in the callback. Define them together, as above, or you will eventually read Severity out of the Message slot.
  • Use UA_ATTRIBUTEID_EVENTNOTIFIER, not VALUE, on the item to monitor. This is a common mistake.
  • You still need UA_Client_run_iterate for events, exactly as for data changes.
  • Add ConditionClassId to the select clause for OEE work: it gives you a portable downtime reason code.

What breaks first

The item is created but no events are delivered BadEventFilterInvalid
A select clause names a field the server's event types do not declare. Reduce to Severity, SourceName, Message and Time, confirm events flow, then add fields back.
Other recipes in this stack

Last verified 7 September 2026 against OPC UA 1.05.07. OPC UA Connect is an educational resource and is not affiliated with the OPC Foundation.

Free download
OPC UA Troubleshooting & Integration Checklist
The field-tested checklist engineers use to diagnose certificates, ports and namespace problems fast, plus a Python (asyncua) client template.
Get the free checklist →
Free, no course, no login