3.4. Notifications support
3.4.1. Overview
Some resources, like sensor readings, change over time. To track these changes, the LwM2M Server can use the Observe operation. This allows it to receive notifications whenever a value changes or meets certain conditions.
See also
For more details about the Observe operation and observation attributes, refer to the LwM2M TS: Core specification.
3.4.2. Notify the library about changes
The LwM2M Server can observe changes in resources using the Observe operation. When resource values change independently of LwM2M operations (such as Write or Create), you must inform the Anjay Lite library manually by calling the anj_core_data_model_changed()
function:
void anj_core_data_model_changed(anj_t *anj,
const anj_uri_path_t *path,
anj_core_change_type_t change_type);
Call this function with the correct change_type
based on what changed:
Use
ANJ_CORE_CHANGE_TYPE_VALUE_CHANGED
when a resource or resource instance value has been updated.Use
ANJ_CORE_CHANGE_TYPE_ADDED
orANJ_CORE_CHANGE_TYPE_DELETED
when an object instance or resource instance has been added or removed.Note
If the removed entity was being observed, its observation will be automatically removed.
Once anj_core_data_model_changed()
is called, the library will respond to
the change during subsequent calls to anj_core_step()
. If the affected
resource is under observation and the configured conditions are met, a Notify
message will be prepared and eventually sent to the LwM2M Server.
Important
anj_core_data_model_changed()
is not only used to support notifications,
but is also essential for other mechanisms, including:
Triggering an Update message that includes a list of all objects and their instances when the set of registered object instances changes.
Reading certain resources that manage registration state, such as the Lifetime, Mute Send, or Notification Storing resources of the Server object.
Note
The number of concurrent observations and the number of entities with
observation attributes are limited by the configuration options:
ANJ_OBSERVE_MAX_OBSERVATIONS_NUMBER
and
ANJ_OBSERVE_MAX_WRITE_ATTRIBUTES_NUMBER
.
3.4.3. Example: Add notifications to a Temperature object
This example shows how to add notification support to a Temperature object. You’ll
use the anj_core_data_model_changed()
function to inform the library when resource
values change.
3.4.3.1. Update resource values
The update_sensor_value()
function updates three resources periodically:
Sensor Value, Min Measured Value, and Max Measured Value.
Call anj_core_data_model_changed()
for each resource whose value changes:
void update_sensor_value(anj_t *anj, const anj_dm_obj_t *obj) {
(void) obj;
temp_obj_ctx_t *ctx = get_ctx();
double prev_temp_value = ctx->sensor_value;
ctx->sensor_value = next_temperature_with_limit(ctx->sensor_value, 0.2);
if (prev_temp_value != ctx->sensor_value) {
anj_core_data_model_changed(anj,
&ANJ_MAKE_RESOURCE_PATH(TEMPERATURE_OID, 0,
RID_SENSOR_VALUE),
ANJ_CORE_CHANGE_TYPE_VALUE_CHANGED);
}
if (ctx->sensor_value < ctx->min_sensor_value) {
ctx->min_sensor_value = ctx->sensor_value;
anj_core_data_model_changed(
anj,
&ANJ_MAKE_RESOURCE_PATH(TEMPERATURE_OID, 0,
RID_MIN_MEASURED_VALUE),
ANJ_CORE_CHANGE_TYPE_VALUE_CHANGED);
}
if (ctx->sensor_value > ctx->max_sensor_value) {
ctx->max_sensor_value = ctx->sensor_value;
anj_core_data_model_changed(
anj,
&ANJ_MAKE_RESOURCE_PATH(TEMPERATURE_OID, 0,
RID_MAX_MEASURED_VALUE),
ANJ_CORE_CHANGE_TYPE_VALUE_CHANGED);
}
}
If any of these resources are observed by the LwM2M Server, the library will send a Notify message when the value changes.
Note
Do not call anj_core_data_model_changed()
when the change is directly
triggered by an LwM2M operation (e.g., Write or Create). In such cases, all
required actions are handled internally by the library.
Warning
It’s crucial to call anj_core_data_model_changed()
only after
ensuring that the subsequent res_read()
call will return the updated
resource value. In this simple example, since res_read()
just returns a
value from memory, the function can be called right after assigning new
values to fields in temp_obj_ctx_t
. Anjay Lite may perform a read
immediately during a call to anj_core_data_model_changed()
, and may
continue reading during future anj_core_step()
calls.
This behavior differs from Anjay 3, where anjay_notify_changed()
and
anjay_notify_instances_changed()
can be invoked at any time, as long as
the new resource values are available before the next call to
anjay_event_loop_run()
.
3.4.3.2. Handle execute operations
The Reset Min and Max Measured Values resource is implemented using res_execute()
. Although it is triggered by an LwM2M command, it changes other resource values, so you must still notify the library:
static int res_execute(anj_t *anj,
const anj_dm_obj_t *obj,
anj_iid_t iid,
anj_rid_t rid,
const char *execute_arg,
size_t execute_arg_len) {
(void) anj;
(void) obj;
(void) iid;
(void) execute_arg;
(void) execute_arg_len;
temp_obj_ctx_t *temp_obj_ctx = get_ctx();
switch (rid) {
case RID_RESET_MIN_MAX_MEASURED_VALUES: {
temp_obj_ctx->min_sensor_value = temp_obj_ctx->sensor_value;
temp_obj_ctx->max_sensor_value = temp_obj_ctx->sensor_value;
anj_core_data_model_changed(
anj,
&ANJ_MAKE_RESOURCE_PATH(TEMPERATURE_OID, 0,
RID_MIN_MEASURED_VALUE),
ANJ_CORE_CHANGE_TYPE_VALUE_CHANGED);
anj_core_data_model_changed(
anj,
&ANJ_MAKE_RESOURCE_PATH(TEMPERATURE_OID, 0,
RID_MAX_MEASURED_VALUE),
ANJ_CORE_CHANGE_TYPE_VALUE_CHANGED);
return 0;
}
default:
break;
}
return ANJ_DM_ERR_NOT_FOUND;
}
Note
It is not required to check whether the value actually changed before calling anj_core_data_model_changed()
. For simplicity, this check is skipped in the res_execute()
callback.
However, doing so helps avoid unnecessary Notify messages—especially for resources that are observed without any conditions (like thresholds). In these cases, the library does not remember the last value it sent, so it may send unnecessary notifications unless your application filters them out.
3.4.3.3. Update function declaration
Update the update_sensor_value()
function to include the anj_t *anj
parameter, since it is required by anj_core_data_model_changed()
:
/**
* @brief Updates the sensor value and adjusts min/max tracked values.
*
* Simulates a new temperature reading for the given object by applying a small
* random fluctuation to the current value. Also updates the minimum and maximum
* recorded values based on the new reading.
*
* @param anj Pointer to the Anjay Lite instance.
* @param obj Pointer to the Temperature Object.
*/
void update_sensor_value(anj_t *anj, const anj_dm_obj_t *obj);
3.4.3.4. Call the function in the main loop
int main(int argc, char *argv[]) {
// ...
while (true) {
anj_core_step(&anj);
update_sensor_value(&anj, get_temperature_obj());
usleep(50 * 1000);
}
return 0;
}
That’s it. Your application now supports Observe/Notify for dynamic resources like temperature readings.