Using callback mode

Updated: April 19, 2023

Use callback functions to provide custom code to be executed when a specific sensor event occurs.

In general when you're using callback mode to access sensor data or status, do the following:

  1. Define your callback functions in your application.
  2. Open a connection to the sensor whose data and status you want access to. See Opening and closing a connection to a sensor unit.
  3. Register your callback functions.
  4. Start streaming from the sensor whose data and status you want access to. See Starting and stopping a sensor unit or Starting an interim data unit. Usually, it's the publishers that control the streaming of interim data. Therefore, subscribers are unlikely to call the start and stop functions.

    As the sensor events that you've registered for occur, the Sensor library invokes your callback functions and processes the data as per your callbacks' implementations.

  5. Stop streaming from the sensor whose data and status you want access to. See Starting and stopping a sensor unit or Starting an interim data unit. Similar to the starting of streaming, it's the publishers that control the streaming of interim data. Therefore, subscribers are unlikely to call the start and stop functions.
  6. Disable your callback functions when you no longer need them.
  7. Close connection to the sensor whose data and status you want access to. See Opening and closing a connection to a sensor unit.

You can register multiple callback functions up to a maximum of sixteen per sensor handle. You can register callback functions for different sensor events, or for the same sensor event. If you register multiple callback functions for one sensor event, then all registered callbacks are invoked when that sensor event occurs.

You can call sensor_register_data_callback() and sensor_register_status_callback() to register callback functions as long as you have a handle to the sensor (from calling sensor_open()). The sensor doesn't have to be started before you can register your callbacks. However if the sensor hasn't started, it's unlikely that there's data available and status changes occurring, so your callback functions are not likely to be invoked.

If you need to pass user-specific information to the callback function, use arg when you register the callback. The Sensor library, when it invokes your callback function, passes this data to it.

Ensure that you release any resources that you still have access to before you disable your callback functions. Once you disable your callback functions from a specified sensor event, the Sensor library no longer invokes the callbacks when that sensor event occurs.

Accessing sensor data

The Sensor library can invoke callback functions to receive data when the following sensor events occur:

SENSOR_EVENT_BUFFER
Indicates that a data buffer is now available. Usually this event is of interest to a publisher that's waiting for an available buffer so that it can store the interim data for publishing.
SENSOR_EVENT_STREAM_DATA
Indicates that a data buffer from a sensor that's streaming is available. Usually this event is of interest to a subscriber that's waiting for data to read.

When you are using callback functions to access sensor data, any memory allocated for the buffer may be released when the callback completes its execution. In callback mode, you access one buffer at a time when the callback executes. The next buffer is available only when the callback function is invoked again. If your callback function returns false, you may still have access to previous data buffers. However, the publisher may run out of buffers for processing if you aren't releasing buffers in a timely manner.

Define your data callback functions

The implementation of your callback function should take into consideration that it's executed for a specific sensor event and that it's running in a thread that's owned by the Sensor library.

The Sensor library frees the data buffer upon return of your callback function if it returns true; your application no longer has access to this data buffer. If your callback function returns false, then you continue to have access to the data buffer, but you must ensure that you release the buffer by calling sensor_return_buffer() when you are finished using it.

The callback functions that your application provides must have the signature as defined by the function prototype sensor_data_callback_t. For example:

...
#define DEFAULT_BUFFERS_CNT         100
#define DEFAULT_CONSUME_LATENCY_US  50
...
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  condvar = PTHREAD_COND_INITIALIZER;
static uint32_t consumeLatency = DEFAULT_CONSUME_LATENCY_US;
static int bufCnt = DEFAULT_BUFFERS_CNT;
static int receivedBuffers;
...
static bool my_sensor_data_callback(sensor_handle_t handle,
                                    sensor_buffer_t* buf,
                                    void* arg)
{
    pthread_mutex_lock(&mutex);
    receivedBuffers++;
    static int64_t last_time = get_time();
    uint64_t new_time = get_time();
    printf("Received buffer %d (%" PRIu64 "us since previous): time %jd format %d size %" PRIu64 "\n",
                                receivedBuffers, new_time-last_time,
                                buf->timestamp, buf->format, buf->size);
    last_time = new_time;
    if (buf->format == SENSOR_FORMAT_VIDEO_NV12) {
        printf(" NV12: %d x %d stride %d chroma %" PRId64 ", %" PRId64 "\n",
               buf->info.video_semiplanar.height,
               buf->info.video_semiplanar.width,
               buf->info.video_semiplanar.stride,
               buf->info.video_semiplanar.chroma_stride,
               buf->info.video_semiplanar.chroma_offset);
     } else if (buf->format == SENSOR_FORMAT_VIDEO_ROI) {
         printf(" ROI: %d x %d type %d roi (%d, %d, %d %d) stride (%d, %d, %d) offset (%d %d %d)\n",
                buf->info.video_roi.width,
                buf->info.video_roi.height,
                buf->info.video_roi.type,
                buf->info.video_roi.roi_x,
                buf->info.video_roi.roi_y,
                buf->info.video_roi.roi_width,
                buf->info.video_roi.roi_height,
                buf->info.video_roi.stride[0],
                buf->info.video_roi.stride[1],
                buf->info.video_roi.stride[2],
                buf->info.video_roi.offset[0],
                buf->info.video_roi.offset[1],
                buf->info.video_roi.offset[2]);
     } else if (buf->format == SENSOR_FORMAT_LIDAR_POLAR) {
         printf(" LIDAR POLAR: data_size %" PRIu64 "\n", buf->info.data.data_size);
     } else if (buf->format == SENSOR_FORMAT_LIDAR_POINT_CLOUD) {
         printf(" LIDAR POINT CLOUD: data_size %" PRIu64 "\n", buf->info.data.data_size);
     } else if (buf->format == SENSOR_FORMAT_RADAR_POLAR) {
         printf(" RADAR POLAR: data_size %" PRIu64 "\n", buf->info.data.data_size);
     }
    usleep(consumeLatency);
    if (receivedBuffers == bufCnt) {
        // send signal to stop streaming
        pthread_cond_signal(&condvar);
    }
    pthread_mutex_unlock(&mutex);

    return true;
}
            

Register your data callback functions

When you register your callback function, you must specify the mode by which you are using the data buffers. You can access the buffers in read-only or read-write modes. The read-only allows you to only read the data from the buffer; you can't modify the data in the buffer. The read-write modes allow you to not only read the data from the buffer, but also to modify the data in the buffer. Specify the mode by which you want access to the buffers as one of the sensor_eventmode_t types in the mode parameter of the sensor_register_data_callback() function.

Register your callback functions with the Sensor library by calling sensor_register_data_callback(). For example:

sensor_error_t err;
sensor_handle_t handle = NULL;
...
err = sensor_register_data_callback(handle,
                                    SENSOR_EVENT_STREAM_DATA,
                                    SENSOR_EVENTMODE_READONLY,
                                    sensor_data_callback,
                                    NULL);
...
            

Disable your data callback functions

When you no longer need your callback functions invoked on a sensor event, disable your callback functions from the Sensor library by calling sensor_disable_data_callback(). For example:

sensor_error_t err;
sensor_handle_t handle = NULL;
...
err = sensor_disable_data_callback(handle, SENSOR_EVENT_STREAM_DATA, sensor_data_callback);
...
            

Ensure that you release any resources that you still have access to before you disable your callback functions. Once you disable your callback functions from a specified sensor event, the Sensor library no longer invokes the callbacks when that sensor event occurs.

Accessing sensor status

The Sensor libarary can invoke callback functions to receive status when the following sensor events occur:

SENSOR_EVENT_STREAM_STATUS
Indicates that stream status information is available. The Sensor library provides the specific status of the stream to you in the status parameter of the callback function. The status can be one of the statuses of type sensor_devstatus_t.

Some statuses may have additional information that's related to the event. For these statuses, the Sensor library specifies the additional information to you in the extra parameter of the callback function.

Define your status callback functions

The callback functions that your application provides must have the signature as defined by the function prototype sensor_status_callback_t. For example:

...
static void sensor_status_callback(sensor_handle_t handle,
                                   sensor_devstatus_t status,
                                   uint16_t extra,
                                   void* arg)
{

  switch (status) {
     case SENSOR_STATUS_CONNECTED:
            printf("Received CONNECTED status\n");
            break;
     case SENSOR_STATUS_STREAMING:
            printf("Received STREAMING status\n");
            break;
     case SENSOR_STATUS_QUEUE_OVERFLOW:
            printf("Received QUEUE_OVERFLOW status with extra %d\n", extra);
            break;
     case SENSOR_STATUS_STREAMING_ACTIVE:
            printf("Received STREAMING_ACTIVE status\n");
            break;
     case SENSOR_STATUS_BUFFER_UNDERFLOW:
            printf("Received BUFFER_UNDERFLOW status\n");
            break;
     case SENSOR_STATUS_STREAMING_ERROR:
            printf("Received STREAMING_ERROR status\n");
            break;
     case SENSOR_STATUS_BUFFER_AVAILABLE:
            printf("Received BUFFER_AVAILABLE status\n");
            break;
     default:
            // Print others that we do not expect
            printf("Received status event %d extra %d\n", status, extra);
            break;
        }
}
...
            

Register your status callback functions

Register your callback functions with the Sensor library by calling sensor_register_data_callback(). For example:

sensor_error_t err;
sensor_handle_t handle = NULL;
...
err = sensor_register_status_callback(handle,
                                      SENSOR_EVENT_STREAM_STATUS,
                                      sensor_status_callback,
                                      NULL);
...
            

Disable your status callback functions

When you no longer need your callback functions invoked on a sensor event, disable your callback functions from the Sensor library by calling sensor_disable_status_callback(). For example:

sensor_error_t err;
sensor_handle_t handle = NULL;
...
err = sensor_disable_status_callback(handle, SENSOR_EVENT_STREAM_STATUS, sensor_status_callback);
...
            

Ensure that you release any resources that you still have access to before you disable your callback functions. Once you disable your callback functions from a specified sensor event, the Sensor library no longer invokes the callbacks when that sensor event occurs.