10.7. Non-IP Data Delivery

10.7.1. General description

Using Non-IP Data Delivery (NIDD) feature allows for efficient communication between IoT devices and a LwM2M server. The communication is performed through the cellular network that ensures security while reducing transmission overhead required by a classic network stack (IP, TCP and UDP protocols), still allowing to send up to 1500 bytes in a single transmission. Utilizing an IP protocol stack for data delivery is considered to be a power hungry process. By using Non-IP Data Delivery instead, the device can reduce its power consumption.

Non-IP Data Delivery feature provides easy support for different modem devices by allowing the user to define device wrappers that will be used to communicate with the hardware. Anjay comes with one implementation of such device wrapper for the Quectel BG96 modem.

10.7.2. Technical documentation

10.7.2.1. Enabling NIDD support

If Non-IP Data Delivery support is available in your version of Anjay, it can be enabled at compile time by enabling the ANJAY_WITH_NIDD macro in the anjay_config.h file or, if using CMake, enabling the corresponding WITH_NIDD CMake option.

Note

The example in this documentation uses the BG96 wrapper for the NIDD driver that must be enabled at compile time by setting the ANJAY_WITH_MODULE_BG96_NIDD macro in the anjay_config.h file or by using WITH_MODULE_bg96_nidd CMake option.

10.7.2.2. Usage example

Note

The full code for the following example can be found in the examples/commercial-features/CF-NIDD directory in Anjay sources. Note that to compile and run it, you need to have access to a commercial version of Anjay that includes the Non-IP Data Delivery feature.

In this example we will use a BG96 wrapper for an NIDD driver that is part of Anjay. To run this example on a Linux-based system you will need to have a Quectel BG96 modem connected to your PC or utilize the Python script found in tests/integration/framework/nidd/modem.py that will simulate BG96 modem and act as a proxy to connect to a LwM2M server.

Note

The Python modem.py script creates a pseudo-terminal device which acts like an AT modem. NIDD network requests generated by Anjay are sent over UDP socket.

Note

To set up the proxy with the Python script run ./modem.py --host HOST_IP_ADDRESS --port PORT_NUMBER command before launching the example. After starting the script it will display the path of the device that simulates the a BG96 modem.

Firstly we will introduce a helper function that will open the device associated with the modem proxy and create an NIDD driver for communication.

 anjay_nidd_driver_t **demo_nidd_driver_create(const char *modem_device) {
     demo_nidd_driver_t *driver =
             (demo_nidd_driver_t *) avs_calloc(1, sizeof(*driver));
     if (!driver) {
         return NULL;
     }

     const anjay_bg96_nidd_config_t config = {
         .system_descriptor = &driver->pts_fd,
         .user_context = driver,
         .modem_getline = modem_getline,
         .modem_write = modem_write,
         .modem_get_parameter = modem_get_parameter
     };
     if (fifo_init(&driver->fifo)) {
         avs_log(tutorial, ERROR, "could not initialize FIFO");
         goto fail;
     }
     if ((driver->pts_fd = open(modem_device, O_RDWR)) < 0) {
         avs_log(tutorial, ERROR, "could not open modem device %s: %s",
                 modem_device, strerror(errno));
         goto fail;
     }

     if (!(driver->bg96_nidd = anjay_bg96_nidd_driver_create(&config))) {
         avs_log(tutorial, ERROR, "could not create AT NIDD driver");
         goto fail;
     }
     return &driver->bg96_nidd;

 fail:
     driver_cleanup(driver);
     return NULL;
 }

Important

If the user does not use the BG96 driver delivered with Anjay, a set of functions has to be to implemented that will allow communication with the modem device. More information about the needed functions can be found here:

This function calls anjay_bg96_nidd_driver_create(...) function that fills anjay_nidd_driver_t structure with callback functions used to integrate with BG96 modem. The structure with the callback functions containing implementation of modem integration layer is returned on success. We call this function before filling anjay_configuration_t structure and passing the returned pointer to .nidd_driver.

 int main(int argc, char *argv[]) {
     if (argc != 3) {
         avs_log(tutorial, ERROR, "usage: %s ENDPOINT_NAME MODEM_PATH", argv[0]);
         return -1;
     }

     anjay_nidd_driver_t **demo_nidd_driver = demo_nidd_driver_create(argv[2]);

     if (!demo_nidd_driver) {
         avs_log(tutorial, ERROR, "Could not create NIDD driver");
         return -1;
     }

     const anjay_configuration_t CONFIG = {
         .endpoint_name = argv[1],
         .in_buffer_size = 4000,
         .out_buffer_size = 4000,
         .msg_cache_size = 4000,
         .nidd_driver = *demo_nidd_driver
     };

     anjay_t *anjay = anjay_new(&CONFIG);
     if (!anjay) {
         avs_log(tutorial, ERROR, "Could not create Anjay object");
         return -1;
     }

     int result = 0;
     // Setup necessary objects
     if (setup_security_object(anjay) || setup_server_object(anjay)) {
         result = -1;
     }

     if (!result) {
         result = anjay_event_loop_run(
                 anjay, avs_time_duration_from_scalar(1, AVS_TIME_S));
     }

     anjay_delete(anjay);
     demo_nidd_driver_cleanup(demo_nidd_driver);
     return result;
 }

Important

If nidd_driver is not NULL, it will enable NIDD transport. If Anjay configuration used does not support Non-IP Data Delivery feature, setting this to a non-NULL will cause an error.

Before calling anjay_event_loop_run we need to set up Security and Server objects.

 static int setup_security_object(anjay_t *anjay) {
     if (anjay_security_object_install(anjay)) {
         return -1;
     }

     const anjay_security_instance_t security_instance = {
         .ssid = 1,
         .server_uri = "coap+nidd://",
         .security_mode = ANJAY_SECURITY_NOSEC
     };

     // Anjay will assign Instance ID automatically
     anjay_iid_t security_instance_id = ANJAY_ID_INVALID;
     if (anjay_security_object_add_instance(anjay, &security_instance,
                                            &security_instance_id)) {
         return -1;
     }

     return 0;
 }

 // Installs Server Object and adds and instance of it.
 // An instance of Server Object provides the data related to a LwM2M Server.
 static int setup_server_object(anjay_t *anjay) {
     if (anjay_server_object_install(anjay)) {
         return -1;
     }

     const anjay_server_instance_t server_instance = {
         // Server Short ID
         .ssid = 1,
         // Client will send Update message often than every 60 seconds
         .lifetime = 60,
         // Disable Default Minimum Period resource
         .default_min_period = -1,
         // Disable Default Maximum Period resource
         .default_max_period = -1,
         // Disable Disable Timeout resource
         .disable_timeout = -1,
         // Sets preferred transport to NIDD
         .binding = "N"
     };

     // Anjay will assign Instance ID automatically
     anjay_iid_t server_instance_id = ANJAY_ID_INVALID;
     if (anjay_server_object_add_instance(anjay, &server_instance,
                                          &server_instance_id)) {
         return -1;
     }

     return 0;
 }

Server URI should be set to coap+nidd://, security mode should be ANJAY_SECURITY_NOSEC and binding in server object should be set to ‘N’.