Example of a Simple Integration Module

struct np_timestamp_functions

This is one of the most simple integration interfaces, so it is a good place to start. This interface tells Nabto Edge how to get the current time from the system. This is used to keep track of retransmissions of internet communication etc. The interface can be found in nabto-embedded-sdk/src/platform/interfaces/np_timestamp.h and looks like this:

struct np_timestamp_functions {
    /**
     * Return current timestamp as milliseconds the timestamp should
     * be a monotonic value which wraps around whenever the value
     * reaches 2^32. The precision is not critical.
     *
     * @param  data  The timestamp object data.
     * @return  The current timestamp in milliseconds.
     */
    uint32_t (*now_ms)(struct np_timestamp* obj);
}

struct np_timestamp {
    const struct np_timestamp_functions* vptr;
    // Pointer to data which is implementation specific
    void* data;
};

The np_timestamp struct defines the modules data (void* data) and the functions (struct np_timestamp_functions* vptr). The data section is a pointer that is fully up to the implementation integration to use and implement or not use at all.

The np_timestampe_functions defines a set of functions that the integration modules supply. For the timestamp module this is very simple since it is only one function uint32_t ts_now_ms(struct np_timestamp* obj)

On Linux this interface could be accomplished by making the following function (please refer to the clock_gettime function):

uint32_t ts_now_ms(struct np_timestamp* obj)
{
    struct timespec spec;
    clock_gettime(CLOCK_REALTIME, &spec);
    return ((spec.tv_sec * 1000) + (spec.tv_nsec / 1000000));
}

To create the np_timestamp_functions table you could do the following:

static struct np_timestamp_functions vtable = {
    .now_ms               = &ts_now_ms
};

And to make a function that setup the np_timestamp struct is would look like:

struct np_timestamp nm_unix_ts_create()
{
    struct np_timestamp ts;
    ts.vptr = &vtable;
    ts.data = NULL;
    return ts;
}

Note this implementation of np_timestamp does not use the user supplied data for anything.

Calling the function above would setup a np_timestamp like this:

To setup the timestamp integration modules the intergrator could now do something like:

np_error_code nabto_device_platform_init(struct nabto_device_context* device, struct nabto_device_mutex* eventMutex) {

    ...

    struct np_timestamp timestampImpl = nm_unix_ts_create();
    nabto_device_integration_set_timestamp_impl(device, &timestampImpl);

    ...

}

Not that the nabto_device_integration_set_timestamp_impl functions all copy the implementation structs even though they are pointers (to be sure that integrators do not make a mistake of deallocating them too soon).

Note: if the ts.data is initialized with allocated user data, this data must be deallocated when the nabto_device_platform_deinit is called. Pointers to the allocated user data can be collected in a struct or similar and be kept via the the nabto_device_integration_set_platform_data utility function and be reached by using the identical get function (see earlier explanation).