LwM2M Client does not respect observer attributes

Hi everyone

We have had the experience on two different LwM2M servers that the Observer attributes are not being respected by the LwM2M client. Reading the Observer attributes we can also see that they are set, but just not respected

pmin:

- Minimum time between updates

- even if the observed value changes, the Observer will not send an update before pmin seconds --> e.g. A changing value will only be updated every 10 minutes, even if it changes every 30 seconds

- Works as intended

pmax:

- Maximum time between updates

- even if the observed value doesn't change, the Observer will send an update after pmax seconds --> e.g. At least one value every 6h, even if it does not change

- works as intended

gt:

- Threshold value

- Observer only sends an update if the observed value EXCEEDS a certain threshold --> e.g. Temperatures under 25°C are ignored. An update is only sent when Value crosses Threshold or after pmax has expired

- does not work as expected

lt:

- Threshold value

- Same as above, but reversed. Only reports values BELOW the threshold --> e.g. Temperatures above 36°C are ignored

- does not work as expected

st:

- Step value

- Observer reports the value if it changes by X from one measurement to the other --> e.g. Change from 36.2°C to 36.4°C is ignored, but a change from 36.2°C to 22.3°C is reported

- does not work as expected

Does anyone have any experience with this issue?

We are working on a custom Board based on the Thingy:91; running Toolchain v2.6.2, modem Firmware v1.3.6, Application based on the LwM2M Client Sample.

This Issue should be reproducible on a Thingy:91 running the LwM2M Client sample though.

I will update this post with more information tomorrow

  • Hi all, I work with   on this issue. Here a few additional infos:

    As already stated, observer atttributes can be set and read. However, the logs do not show the actual value when attributes are set or updated (except for pmin & pmax). Here a sample log output of a setting of attributes after device restart:


    [00:00:27.355,285] <dbg> net_lwm2m_observation: lwm2m_write_attr_handler: Add pmin to 60
    [00:00:27.355,316] <dbg> net_lwm2m_observation: lwm2m_write_attr_handler: Add pmax to 300
    [00:00:27.355,346] <dbg> net_lwm2m_observation: lwm2m_write_attr_handler: Add gt to *float*

    in this case, 'gt' was set to 700 (could also be read as 700 using the Discover-RPC call). The same behaviour is observed when an observer attribute is updated (e.g. 'Update gt to *float*')

    Also, this morning, I've gotten an error message for the first time; `net_lwm2m_observation: No attribute found!`, behaviour as always (pmin & pmax respected, rest ignored).

    [00:00:14.854,278] <inf> app_lwm2m_client: LwM2M is connecting to server
    [00:00:14.854,980] <dbg> net_lwm2m_registry: lwm2m_engine_get: path:0/0/2/0, level 3, buf:0x2002d4c3, buflen:1
    [00:00:14.903,076] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:4/0/4, buf:0x20019c22, len:14
    [00:00:14.903,137] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:4/0/7, buf:0x20019ec2, len:12
    [00:00:14.903,198] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:3/0/3, buf:0x2001a0fa, len:18
    [00:00:14.903,228] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:4/0/8, buf:0x2003403c, len:4
    [00:00:14.903,289] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:4/0/9, buf:0x2003403e, len:2
    [00:00:14.903,350] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:4/0/10, buf:0x2003403e, len:2
    [00:00:14.903,381] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:4/0/12, buf:0x2003403e, len:2
    [00:00:15.168,975] <inf> net_lwm2m_engine: Connected, sock id 2
    [00:00:15.169,616] <dbg> net_lwm2m_registry: lwm2m_engine_get: path:1/0/1/0, level 3, buf:0x2002d4e8, buflen:4
    [00:00:15.170,410] <dbg> net_lwm2m_rd_client: sm_send_registration: registration sent [34.65.106.240]
    [00:00:15.172,515] <dbg> net_lwm2m_rd_client: lwm2m_rd_client_service: State: 8
    [00:00:15.284,057] <dbg> net_lwm2m_rd_client: do_registration_reply_cb: Registration callback (code:2.1)
    [00:00:15.284,973] <dbg> net_lwm2m_registry: lwm2m_engine_get: path:1/0/1/0, level 3, buf:0x2002d350, buflen:4
    [00:00:15.285,705] <dbg> app_lwm2m_client: rd_client_event: Registration complete
    [00:00:15.285,919] <inf> net_lwm2m_rd_client: Registration Done (EP='HLiOp93gmY')
    [00:00:15.286,132] <dbg> net_lwm2m_message_handling: lwm2m_udp_receive: reply 0x20012290 handled and removed
    [00:00:15.286,193] <dbg> net_lwm2m_rd_client: lwm2m_rd_client_service: State: 9
    [00:00:15.286,895] <inf> app_lwm2m_client: LwM2M is connected to server
    [00:00:15.286,895] <dbg> net_lwm2m_registry: lwm2m_engine_get: path:1/0/1/0, level 3, buf:0x2002f8c0, buflen:4
    [00:00:15.291,717] <inf> app_lwm2m_client: Obtained date-time from modem
    [00:00:15.291,748] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:3/0/13, buf:0x2002df5c, len:4
    [00:00:15.454,223] <dbg> net_lwm2m_observation: engine_observe_node_init: OBSERVER ADDED 3303/1/5700/0(3)
    [00:00:15.454,345] <dbg> net_lwm2m_observation: engine_observe_node_init: token:'f3f22de35cec7f90' addr:34.65.106.240
    [00:00:15.525,604] <dbg> net_lwm2m_message_handling: lwm2m_engine_default_content_format: No accept option given. Assume SenML CBOR.
    [00:00:15.525,848] <dbg> net_lwm2m_observation: lwm2m_write_attr_handler: Add pmin to 121
    [00:00:15.525,909] <dbg> net_lwm2m_observation: lwm2m_write_attr_handler: Add pmax to 301
    [00:00:15.525,939] <dbg> net_lwm2m_observation: lwm2m_write_attr_handler: Add gt to *float*
    [00:00:15.525,970] <dbg> net_lwm2m_observation: lwm2m_write_attr_handler: Add lt to *float*
    [00:00:15.526,000] <dbg> net_lwm2m_observation: lwm2m_write_attr_handler: Add st to *float*
    [00:00:19.172,851] <dbg> net_lwm2m_rd_client: lwm2m_rd_client_service: State: 9
    [00:00:21.268,371] <dbg> net_lwm2m_rd_client: lwm2m_rd_client_service: State: 9
    [00:00:21.268,432] <inf> lwm2m_rai: RAI enabled
    [00:00:21.275,177] <dbg> app_lwm2m_client: rd_client_event: Queue mode RX window closed
    [00:00:39.355,468] <inf> app_lwm2m_client: Modem Enter PSM, time 89983950
    [00:00:59.200,073] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:3303/1/5700, buf:0x20034058, len:8
    [00:00:59.200,103] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:3303/1/5518, buf:0x20033fc8, len:8
    [00:00:59.200,195] <dbg> net_lwm2m_observation: lwm2m_notify_observer_path: NOTIFY EVENT 3303/1/5700
    [00:00:59.209,167] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:3304/1/5700, buf:0x20034058, len:8
    [00:00:59.209,197] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:3304/1/5518, buf:0x20033fc8, len:8
    [00:00:59.209,533] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:3316/0/5700, buf:0x20034058, len:8
    [00:01:49.218,750] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:3303/1/5700, buf:0x20034058, len:8
    [00:01:49.218,780] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:3303/1/5518, buf:0x20033fc8, len:8
    [00:01:49.218,872] <dbg> net_lwm2m_observation: lwm2m_notify_observer_path: NOTIFY EVENT 3303/1/5700
    [00:01:49.227,874] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:3304/1/5700, buf:0x20034058, len:8
    [00:01:49.227,905] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:3304/1/5518, buf:0x20033fc8, len:8
    [00:01:49.228,240] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:3316/0/5700, buf:0x20034058, len:8
    [00:02:16.454,437] <dbg> net_lwm2m_message_handling: generate_notify_message: [MANUAL] NOTIFY MSG START: 3303/1/5700(3) token:'f3f22de35cec7f90' [34.65.106.240] 136454
    [00:02:16.456,207] <dbg> net_lwm2m_message_handling: generate_notify_message: NOTIFY MSG: SENT
    [00:02:16.456,390] <dbg> net_lwm2m_rd_client: lwm2m_rd_client_service: State: 9
    [00:02:16.466,430] <dbg> app_lwm2m_client: rd_client_event: Registration update started
    [00:02:16.567,047] <dbg> net_lwm2m_rd_client: lwm2m_rd_client_service: State: 11
    [00:02:16.567,077] <dbg> net_lwm2m_engine: lwm2m_engine_connection_resume: Resume suspended connection
    [00:02:16.567,626] <dbg> net_lwm2m_registry: lwm2m_engine_get: path:1/0/1/0, level 3, buf:0x2002d4f0, buflen:4
    [00:02:16.567,810] <dbg> net_lwm2m_rd_client: sm_send_registration: registration sent [34.65.106.240]
    [00:02:16.567,901] <dbg> net_lwm2m_rd_client: lwm2m_rd_client_service: State: 12
    [00:02:16.645,141] <dbg> net_lwm2m_registry: lwm2m_engine_set: path:4/0/2, buf:0x20034096, len:2
    [00:02:18.476,165] <inf> net_lwm2m_rd_client: Update callback (code:2.4)
    [00:02:18.476,226] <dbg> app_lwm2m_client: rd_client_event: Registration update complete
    [00:02:18.476,318] <inf> net_lwm2m_rd_client: Update Done
    [00:02:18.476,531] <dbg> net_lwm2m_message_handling: lwm2m_udp_receive: reply 0x200122a8 handled and removed
    [00:02:18.476,593] <dbg> net_lwm2m_rd_client: lwm2m_rd_client_service: State: 9
    [00:02:18.487,304] <dbg> net_lwm2m_message_handling: lwm2m_engine_default_content_format: No accept option given. Assume SenML CBOR.
    [00:02:18.487,426] <dbg> net_lwm2m_observation: lwm2m_write_attr_handler: Add gt to *float*
    [00:02:18.498,382] <dbg> net_lwm2m_message_handling: lwm2m_engine_default_content_format: No accept option given. Assume SenML CBOR.
    [00:02:18.506,652] <dbg> net_lwm2m_message_handling: lwm2m_engine_default_content_format: No accept option given. Assume SenML CBOR.
    [00:02:18.506,683] <err> net_lwm2m_observation: No attribute found!
    [00:02:18.564,758] <dbg> net_lwm2m_message_handling: notify_message_reply_cb: NOTIFY ACK type:2 code:0.0 reply_token:'f3f22de35cec7f90'
    [00:02:18.564,971] <dbg> net_lwm2m_message_handling: lwm2m_udp_receive: reply 0x20012290 handled and removed
    [00:02:20.575,500] <dbg> net_lwm2m_rd_client: lwm2m_rd_client_service: State: 9
    [00:02:22.507,995] <dbg> net_lwm2m_rd_client: lwm2m_rd_client_service: State: 9
    [00:02:22.514,770] <dbg> app_lwm2m_client: rd_client_event: Queue mode RX window closed

    Thanks in advance for any help

  • Thanks for reporting this issue.

    Not sure which NCS version are you based on? Looks this is related to Zephyr lwm2m client library which has source codes located in ncs/zephyr/subsys/net/lib/lwm2m/lwm2m_observation.c.

    I will give it a try tomorrow.

    Best regards,

    Charlie

  • Hi Charlie

    Thanks for your answer.

    Have you been able to reproduce the issue?

    I'm starting to suspect that we messed up something somewhere while making our custom application. Since we only ever tested pmin and pmax, I guess somewhere the other 3 observer attributes broke, but I'm not sure. Is there something you have to specifically set so the attributes are set? I assumed that if pmin and pmax work, the rest should work as well.

    Thanks for any pointers on where to look.

    Best regards,

    Alan

  • Hi,

    I checked our official sample NCS 2.7.0 Cellular: LwM2M Client (nordicsemi.com) with Coiote LwM2M server, looks all the observer attributes work as expected.

    Here is one example for Push button Digital Input Counter: pimin=10, pmax=20, st=2.

    Observe the change in a widget:

    When I do not push the button, the update interval is 20s. When I push the button two times to increase the counter from 1 to 3, the update triggered in 10s.

    Please have a try with your setup. 

    Best regards,

    Charlie

  • Hi Charlie,

    Thanks for taking the time to try to reproduce the bug. We started working with NCS 2.4.2, then ported the application to NCS 2.6.0, which we are currently working with. Do you recommend updating to NCS 2.7.0?

    We are now troubleshooting with different servers with the base LwM2M Client sample and our custom application and will keep you updated on our findings.

    General question: Which steps are necessary for adding a new, fully functional LwM2M object?

    Is it more than just creating the object? example for device object

    Path: C:\ncs\v2.6.0\zephyr\subsys\net\lib\lwm2m\lwm2m_obj_device.c:

    static struct lwm2m_engine_obj_inst *device_create(uint16_t obj_inst_id)
    {
                    int i = 0, j = 0;
    
                    init_res_instance(res_inst, ARRAY_SIZE(res_inst));
    
                    /* initialize instance resource data */
                    INIT_OBJ_RES_OPTDATA(DEVICE_MANUFACTURER_ID, res, i, res_inst, j);
                    INIT_OBJ_RES_OPTDATA(DEVICE_MODEL_NUMBER_ID, res, i, res_inst, j);
                    INIT_OBJ_RES_OPTDATA(DEVICE_SERIAL_NUMBER_ID, res, i, res_inst, j);
                    INIT_OBJ_RES_OPTDATA(DEVICE_FIRMWARE_VERSION_ID, res, i, res_inst, j);
                    INIT_OBJ_RES_EXECUTE(DEVICE_REBOOT_ID, res, i, NULL);
                    INIT_OBJ_RES_EXECUTE(DEVICE_FACTORY_DEFAULT_ID, res, i, NULL);
                    INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_AVAILABLE_POWER_SOURCES_ID, res, i,
                                                                      res_inst, j, DEVICE_PWRSRC_MAX, false);
                    INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_POWER_SOURCE_VOLTAGE_ID, res, i,
                                                                      res_inst, j, DEVICE_PWRSRC_MAX, false);
                    INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_POWER_SOURCE_CURRENT_ID, res, i,
                                                                      res_inst, j, DEVICE_PWRSRC_MAX, false);
                    INIT_OBJ_RES_OPTDATA(DEVICE_BATTERY_LEVEL_ID, res, i, res_inst, j);
                    INIT_OBJ_RES_OPTDATA(DEVICE_MEMORY_FREE_ID, res, i, res_inst, j);
                    error_code_ri = &res_inst[j];
                    INIT_OBJ_RES_MULTI_DATA(DEVICE_ERROR_CODE_ID, res, i,
                                                                   res_inst, j, DEVICE_ERROR_CODE_MAX, false,
                                                                   error_code_list, sizeof(*error_code_list));
                    INIT_OBJ_RES_EXECUTE(DEVICE_RESET_ERROR_CODE_ID, res, i,
                                                        reset_error_list_cb);
                    INIT_OBJ_RES_OPT(DEVICE_CURRENT_TIME_ID, res, i, res_inst, j, 1, false,
                                                   true, current_time_read_cb, current_time_pre_write_cb,
                                                   NULL, current_time_post_write_cb, NULL);
                    INIT_OBJ_RES_OPTDATA(DEVICE_UTC_OFFSET_ID, res, i, res_inst, j);
                    INIT_OBJ_RES_OPTDATA(DEVICE_TIMEZONE_ID, res, i, res_inst, j);
                    INIT_OBJ_RES_DATA(DEVICE_SUPPORTED_BINDING_MODES_ID, res, i,
                                                     res_inst, j, binding_mode, DEVICE_STRING_SHORT);
                    INIT_OBJ_RES_OPTDATA(DEVICE_TYPE_ID, res, i, res_inst, j);
                    INIT_OBJ_RES_OPTDATA(DEVICE_HARDWARE_VERSION_ID, res, i, res_inst, j);
                    INIT_OBJ_RES_OPTDATA(DEVICE_SOFTWARE_VERSION_ID, res, i, res_inst, j);
                    INIT_OBJ_RES_OPTDATA(DEVICE_BATTERY_STATUS_ID, res, i, res_inst, j);
                    INIT_OBJ_RES_OPTDATA(DEVICE_MEMORY_TOTAL_ID, res, i, res_inst, j);
                    INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_EXT_DEV_INFO_ID, res, i, res_inst, j,
                                                                      DEVICE_EXT_DEV_INFO_MAX, false);
    
                    inst.resources = res;
                    inst.resource_count = i;
    
                    LOG_DBG("Create LWM2M device instance: %d", obj_inst_id);
                    return &inst;
    }
    
    static int lwm2m_device_init(void)
    {
                    struct lwm2m_engine_obj_inst *obj_inst = NULL;
                    int ret = 0;
    
                    /* Set default values */
                    time_offset = 0U;
                    lwm2m_engine_get_binding(binding_mode);
    
                    /* initialize the device field data */
                    device.obj_id = LWM2M_OBJECT_DEVICE_ID;
                    device.version_major = DEVICE_VERSION_MAJOR;
                    device.version_minor = DEVICE_VERSION_MINOR;
                    device.is_core = true;
                    device.fields = fields;
                    device.field_count = ARRAY_SIZE(fields);
                    device.max_instance_count = 1U;
                    device.create_cb = device_create;
                    lwm2m_register_obj(&device);
    
                    /* auto create the only instance */
                    ret = lwm2m_create_obj_inst(LWM2M_OBJECT_DEVICE_ID, 0, &obj_inst);
                    if (ret < 0) {
                                   LOG_DBG("Create LWM2M instance 0 error: %d", ret);
                    }
    
                    /* Create the default error code resource instance */
                    lwm2m_device_add_err(0);
    
                    /* call device_periodic_service() every 10 seconds */
                    ret = lwm2m_engine_add_service(device_periodic_service,
                                                                          DEVICE_SERVICE_INTERVAL_MS);
                    return ret;
    }
    

    Because that's what we did for all our objects and everything else works. We shouldn't have to initialise these observer attributes in a special way, or should we?

Related