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.
| Stack | open62541 |
|---|---|
| Version | 1.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
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.
BadEventFilterInvalidA 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
Concepts on this page
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.