3.2. Installing mandatory Objects

To connect to a LwM2M server and handle incoming packets, the client must support the following mandatory LwM2M Objects:

Anjay Lite provides pre-implemented modules for all three objects, making setup straightforward.

Note

Users can still provide their own implementation of these Objects if needed — Anjay Lite remains fully flexible.

When Anjay Lite is first instantiated (as in our previous hello world example), it has no knowledge about the Data Model, i.e., no LwM2M Objects are registered within it. You must explicitly install the required Objects, as shown below.

3.2.1. Installing Objects

Use the following functions to install the Objects:

  • anj_dm_security_obj_install()

  • anj_dm_server_obj_install()

  • anj_dm_device_obj_install()

Important

To use these function you must include the following headers:

  • anj/dm/security_object.h

  • anj/dm/server_object.h

  • anj/dm/device_object.h

3.2.2. Setting up Security, Server and Device Objects

This section shows how to implement and register the mandatory Objects: Security, Server, and Device. It builds upon the setup from the previous tutorial.

3.2.2.1. Security Object

The Security Object holds connection parameters for the LwM2M server. In this example, we configure a non-secure connection to the Coiote IoT Device Management platform. A secure connection setup will be described in a later section.

To use Coiote:

  • Create an account at avsystem.com/coiote-iot-device-management-platform.

  • Add your device entry in the Coiote interface using the following URI for the connection: coap://eu.iot.avsystem.cloud:5683

If you are using another server, replace the URI with your target address.

// Installs Security Object and adds an instance of it.
// An instance of Security Object provides information needed to connect to
// LwM2M server.
static int install_security_obj(anj_t *anj,
                                anj_dm_security_obj_t *security_obj) {
    anj_dm_security_instance_init_t security_inst = {
        .ssid = 1,
        .server_uri = "coap://eu.iot.avsystem.cloud:5683",
        .security_mode = ANJ_DM_SECURITY_NOSEC
    };
    anj_dm_security_obj_init(security_obj);
    if (anj_dm_security_obj_add_instance(security_obj, &security_inst)
            || anj_dm_security_obj_install(anj, security_obj)) {
        return -1;
    }
    return 0;
}

3.2.2.2. Server Object

The Server Object defines registration parameters like lifetime and binding mode.

// Installs Server Object and adds an instance of it.
// An instance of Server Object provides the data related to a LwM2M Server.
static int install_server_obj(anj_t *anj, anj_dm_server_obj_t *server_obj) {
    anj_dm_server_instance_init_t server_inst = {
        .ssid = 1,
        .lifetime = 50,
        .binding = "U",
        .bootstrap_on_registration_failure = &(bool) { false },
    };
    anj_dm_server_obj_init(server_obj);
    if (anj_dm_server_obj_add_instance(server_obj, &server_inst)
            || anj_dm_server_obj_install(anj, server_obj)) {
        return -1;
    }
    return 0;
}

Both Security and Server instances are linked together by the Short Server ID Resource (ssid). That is why the ssid value must match between the Security and Server instances.

3.2.2.3. Device Object

The Device Object provides metadata about the device.

// Installs Device Object and adds an instance of it.
// An instance of Device Object provides the data related to a device.
static int install_device_obj(anj_t *anj, anj_dm_device_obj_t *device_obj) {
    anj_dm_device_object_init_t device_obj_conf = {
        .firmware_version = "0.1"
    };
    return anj_dm_device_obj_install(anj, device_obj, &device_obj_conf);
}

3.2.2.4. Integrate Object Installation

Once the installation functions are implemented, call them from your main() function:

int main(int argc, char *argv[]) {
    if (argc != 2) {
        log(L_ERROR, "No endpoint name given");
        return -1;
    }

    anj_t anj;
    anj_dm_device_obj_t device_obj;
    anj_dm_server_obj_t server_obj;
    anj_dm_security_obj_t security_obj;

    anj_configuration_t config = {
        .endpoint_name = argv[1]
    };
    if (anj_core_init(&anj, &config)) {
        log(L_ERROR, "Failed to initialize Anjay Lite");
        return -1;
    }

    if (install_device_obj(&anj, &device_obj)
            || install_security_obj(&anj, &security_obj)
            || install_server_obj(&anj, &server_obj)) {
        return -1;
    }

    while (true) {
        anj_core_step(&anj);
        usleep(50 * 1000);
    }
    return 0;
}

Note

Complete code of this example can be found in examples/tutorial/BC-MandatoryObjects subdirectory of main Anjay Lite project repository.

3.2.2.4.1. Logs example

After running the client, you should see registration successful, location = /rd/<server-dependent identifier> once and registration successfully updated every 25 seconds in logs. It means, that the client has connected to the server and successfully sends Update messages. You can now perform operations like Read from the server side.

3.2.3. Application events

The example code shown above covers events managed internally by the Anjay Lite library. However, most real-world applications also need to handle their own logic. How to implement application-specific functionality will be explained in the following sections.

3.2.4. Coiote experience

At this stage, you can log in to Coiote IoT Device Management and open the Device Center for your registered device to explore the platform functionality. Check the Data Model tab to see which LwM2M Objects are currently exposed. You will notice that the Server and Device objects are visible, but the Security object is not. This is expected behavior defined by the LwM2M specification — the Security object is neither readable nor discoverable from the device to protect sensitive configuration data.