Correctly implement Zephyr sensor shell stream for custom driver?

Hi,

I am trying to enable a sensor shell stream from an IMU connected over I2C. I am using a k_timer to get the sensor values and not DRDY since DRDY was not connected on an older version of the board I have. Here are some observations from USB shell:

1. When my sensor API is something like this, it gives sensor data correctly using `sensor get..`, however, it does not stream using 'sensor trig...` followed by `sensor stream...`. 

static int lsm6dsr_get_decoder(const struct device *dev, const struct sensor_decoder_api **api)
{
    *api = &__sensor_default_decoder;
    return 0;
}

static const struct sensor_driver_api lsm6dsr_api = {
    .attr_set = lsm6dsr_attr_set,
    .sample_fetch = lsm6dsr_sample_fetch,
    .channel_get = lsm6dsr_channel_get,
    .trigger_set = lsm6dsr_trigger_set,
    .get_decoder = lsm6dsr_get_decoder
};

uart:~$ sensor get LSM6DSR accel_xyz
channel type=3(accel_xyz) index=0 shift=1 num_samples=1 value=137052246093ns, (0.015440, -0.027160, 1.030455)
uart:~$ sensor get LSM6DSR accel_xyz
channel type=3(accel_xyz) index=0 shift=0 num_samples=1 value=143734680175ns, (0.998045, 0.048521, 0.084898)
uart:~$ sensor get LSM6DSR accel_xyz
channel type=3(accel_xyz) index=0 shift=1 num_samples=1 value=144496673583ns, (1.000182, 0.056273, 0.098814)
uart:~$ sensor get LSM6DSR accel_xyz
channel type=3(accel_xyz) index=0 shift=0 num_samples=1 value=145070739746ns, (0.994994, 0.051878, 0.097716)
uart:~$ sensor get LSM6DSR accel_xyz
channel type=3(accel_xyz) index=0 shift=0 num_samples=1 value=145834075927ns, (0.978636, 0.031798, 0.081847)

2. When I implement a custom decoder and submit function, I see an endless stream of data. However, it only has the first sample populated in the stream endlessly for the `sensor get..` command. `sensor stream ...` or `sensor trig ...` does not work, just freezes and gives a blank shell output.  

static void lsm6dsr_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
{
    struct lsm6dsr_data *data = dev->data;
    uint8_t *buf;
    uint32_t buf_len;
    int ret;

    /* Check if we have enough buffer space */
    ret = rtio_sqe_rx_buf(iodev_sqe, sizeof(struct minimal_buffer),
                         sizeof(struct minimal_buffer),
                         &buf, &buf_len);

    if (ret != 0) {
        LOG_ERR("Failed to get buffer: %d", ret);
        rtio_iodev_sqe_err(iodev_sqe, -ENOMEM);
        // Stop the timer to prevent further submit calls
        k_timer_stop(&data->timer);

        // Clear the handler to ensure no more callbacks
        data->timer_handler = NULL;

        LOG_ERR("Critical error at buffer grab, stopping sensor stream");
        return;
    }

    /* Fetch the latest sample */
    ret = lsm6dsr_sample_fetch(dev, SENSOR_CHAN_ALL);
    if (ret != 0) {
        LOG_ERR("Failed to fetch sample: %d", ret);
        rtio_iodev_sqe_err(iodev_sqe, ret);
        // Stop the timer to prevent further submit calls
        k_timer_stop(&data->timer);

        // Clear the handler to ensure no more callbacks
        data->timer_handler = NULL;

        LOG_ERR("Critical error at fetch, stopping sensor stream");
        return;
    }

    /* Use a local buffer first to avoid alignment issues */
    struct minimal_buffer local_buffer;

    /* Set timestamp safely using memcpy */
    uint64_t timestamp = k_uptime_get() * 1000000; /* Convert to nanoseconds */
    memcpy(&local_buffer.timestamp, &timestamp, sizeof(uint64_t));

    /* Get channel type from the request */
    const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
    if (cfg && cfg->count > 0) {
        enum sensor_channel chan_type = cfg->channels[0].chan_type;

        /* Lock mutex before accessing sensor data */
        k_mutex_lock(&data->mutex, K_FOREVER);

        if (chan_type == SENSOR_CHAN_ACCEL_XYZ) {
            /* Convert floating point accelerometer data to fixed point */
            q31_t x = (q31_t)(data->scaled_sample.acc_x * (float)(1 << 31));
            q31_t y = (q31_t)(data->scaled_sample.acc_y * (float)(1 << 31));
            q31_t z = (q31_t)(data->scaled_sample.acc_z * (float)(1 << 31));

            /* Copy values safely using memcpy */
            memcpy(&local_buffer.values[0], &x, sizeof(q31_t));
            memcpy(&local_buffer.values[1], &y, sizeof(q31_t));
            memcpy(&local_buffer.values[2], &z, sizeof(q31_t));

            LOG_INF("LSM6DSR accel data: %f, %f, %f",
                   data->scaled_sample.acc_x,
                   data->scaled_sample.acc_y,
                   data->scaled_sample.acc_z);
        }
        else if (chan_type == SENSOR_CHAN_GYRO_XYZ) {
            /* Convert floating point gyroscope data to fixed point */
            q31_t x = (q31_t)(data->scaled_sample.gyr_x * (float)(1 << 31));
            q31_t y = (q31_t)(data->scaled_sample.gyr_y * (float)(1 << 31));
            q31_t z = (q31_t)(data->scaled_sample.gyr_z * (float)(1 << 31));

            /* Copy values safely using memcpy */
            memcpy(&local_buffer.values[0], &x, sizeof(q31_t));
            memcpy(&local_buffer.values[1], &y, sizeof(q31_t));
            memcpy(&local_buffer.values[2], &z, sizeof(q31_t));

            LOG_INF("LSM6DSR gyro data: %f, %f, %f",
                   data->scaled_sample.gyr_x,
                   data->scaled_sample.gyr_y,
                   data->scaled_sample.gyr_z);
        }
        else {
            /* Default values for unsupported channels */
            q31_t val1 = 1000000;
            q31_t val2 = 1000000;
            q31_t val3 = 1000000;

            memcpy(&local_buffer.values[0], &val1, sizeof(q31_t));
            memcpy(&local_buffer.values[1], &val2, sizeof(q31_t));
            memcpy(&local_buffer.values[2], &val3, sizeof(q31_t));

            LOG_WRN("Unsupported channel type: %d", chan_type);
        }

        k_mutex_unlock(&data->mutex);
    }
    else {
        /* Default values if no channel info */
        q31_t val1 = 1000000;
        q31_t val2 = 2000000;
        q31_t val3 = 3000000;

        memcpy(&local_buffer.values[0], &val1, sizeof(q31_t));
        memcpy(&local_buffer.values[1], &val2, sizeof(q31_t));
        memcpy(&local_buffer.values[2], &val3, sizeof(q31_t));
    }

    /* Copy the entire local buffer to the output buffer */
    memcpy(buf, &local_buffer, sizeof(struct minimal_buffer));

    /* Complete the RTIO operation */
    rtio_iodev_sqe_ok(iodev_sqe, sizeof(struct minimal_buffer));
}


// This is to pass values to the sensor shell callback for printing values
static bool lsm6dsr_has_trigger(const uint8_t *buffer, enum sensor_trigger_type trigger)
{
    /* Return true when trigger is set */
    return (trigger == SENSOR_TRIG_TIMER || trigger == SENSOR_TRIG_DATA_READY);
}

static int lsm6dsr_get_decoder(const struct device *dev, const struct sensor_decoder_api **api)
{
    extern const struct sensor_decoder_api minimal_three_axis_decoder;
    *api = &minimal_three_axis_decoder;
    return 0;
}


static const struct sensor_driver_api lsm6dsr_api = {
    .attr_set = lsm6dsr_attr_set,
    .sample_fetch = lsm6dsr_sample_fetch,
    .channel_get = lsm6dsr_channel_get,
#if CONFIG_SENSOR_SHELL_STREAM
    .trigger_set = lsm6dsr_trigger_set,
    .get_decoder = lsm6dsr_get_decoder,
    .submit = lsm6dsr_submit,
#endif
};

channel type=3(accel_xyz) index=807 shift=0 num_samples=1 value=14824000000ns, (-0.011474, 0.007812, -1.000000)
channel type=3(accel_xyz) index=808 shift=0 num_samples=1 value=14824000000ns, (-0.011474, 0.007812, -1.000000)
channel type=3(accel_xyz) index=809 shift=0 num_samples=1 value=14824000000ns, (-0.011474, 0.007812, -1.000000)
channel type=3(accel_xyz) index=810 shift=0 num_samples=1 value=14824000000ns, (-0.011474, 0.007812, -1.000000)
channel type=3(accel_xyz) index=811 shift=0 num_samples=1 value=14824000000ns, (-0.011474, 0.007812, -1.000000)
channel type=3(accel_xyz) index=812 shift=0 num_samples=1 value=14824000000ns, (-0.011474, 0.007812, -1.000000)
channel type=3(accel_xyz) index=813 shift=0 num_samples=1 value=14824000000ns, (-0.011474, 0.007812, -1.000000)
channel type=3(accel_xyz) index=814 shift=0 num_samples=1 value=14824000000ns, (-0.011474, 0.007812, -1.000000)
channel type=3(accel_xyz) index=815 shift=0 num_samples=1 value=14824000000ns, (-0.011474, 0.007812, -1.000000)
channel type=3(accel_xyz) index=816 shift=0 num_samples=1 value=14824000000ns, (-0.011474, 0.007812, -1.000000)
channel type=3(accel_xyz) index=817 shift=0 num_samples=1 value=14824000000ns, (-0.011474, 0.007812, -1.000000)
channel type=3(accel_xyz) index=818 shift=0 num_samples=1 value=14824000000ns, (-0.011474, 0.007812, -1.000000)

My repository is attached. Please help me implement the sensor stream function for the sensor in the correct manner.

Build settings:

SDK and toolchain: 2.8.0

board: nrf52840dk_nrf52840

overlay: euryale.overlay, usb.overlay 

config: prj.conf, overlay-usb.conf

build: optimize for debugging 

Thanks,

Devang

 shasta_sensor_shell_stream.zip

Related