1 /*
2 * Copyright (c) 2018 Diego Sueiro
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <ctype.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/rtio/rtio.h>
15 #include <zephyr/shell/shell.h>
16 #include <zephyr/sys/iterable_sections.h>
17 #include <zephyr/sys/util.h>
18
19 #include "sensor_shell.h"
20
21 LOG_MODULE_REGISTER(sensor_shell, CONFIG_SENSOR_LOG_LEVEL);
22
23 #define SENSOR_GET_HELP \
24 "Get sensor data. Channel names are optional. All channels are read " \
25 "when no channels are provided. Syntax:\n" \
26 "<device_name> <channel name 0> .. <channel name N>"
27
28 #define SENSOR_STREAM_HELP \
29 "Start/stop streaming sensor data. Data ready trigger will be used if no triggers " \
30 "are provided. Syntax:\n" \
31 "<device_name> on|off <trigger name> incl|drop|nop"
32
33 #define SENSOR_ATTR_GET_HELP \
34 "Get the sensor's channel attribute. Syntax:\n" \
35 "<device_name> [<channel_name 0> <attribute_name 0> .. " \
36 "<channel_name N> <attribute_name N>]"
37
38 #define SENSOR_ATTR_SET_HELP \
39 "Set the sensor's channel attribute.\n" \
40 "<device_name> <channel_name> <attribute_name> <value>"
41
42 #define SENSOR_INFO_HELP "Get sensor info, such as vendor and model name, for all sensors."
43
44 #define SENSOR_TRIG_HELP \
45 "Get or set the trigger type on a sensor. Currently only supports `data_ready`.\n" \
46 "<device_name> <on/off> <trigger_name>"
47
48 static const char *sensor_channel_name[SENSOR_CHAN_COMMON_COUNT] = {
49 [SENSOR_CHAN_ACCEL_X] = "accel_x",
50 [SENSOR_CHAN_ACCEL_Y] = "accel_y",
51 [SENSOR_CHAN_ACCEL_Z] = "accel_z",
52 [SENSOR_CHAN_ACCEL_XYZ] = "accel_xyz",
53 [SENSOR_CHAN_GYRO_X] = "gyro_x",
54 [SENSOR_CHAN_GYRO_Y] = "gyro_y",
55 [SENSOR_CHAN_GYRO_Z] = "gyro_z",
56 [SENSOR_CHAN_GYRO_XYZ] = "gyro_xyz",
57 [SENSOR_CHAN_MAGN_X] = "magn_x",
58 [SENSOR_CHAN_MAGN_Y] = "magn_y",
59 [SENSOR_CHAN_MAGN_Z] = "magn_z",
60 [SENSOR_CHAN_MAGN_XYZ] = "magn_xyz",
61 [SENSOR_CHAN_DIE_TEMP] = "die_temp",
62 [SENSOR_CHAN_AMBIENT_TEMP] = "ambient_temp",
63 [SENSOR_CHAN_PRESS] = "press",
64 [SENSOR_CHAN_PROX] = "prox",
65 [SENSOR_CHAN_HUMIDITY] = "humidity",
66 [SENSOR_CHAN_LIGHT] = "light",
67 [SENSOR_CHAN_IR] = "ir",
68 [SENSOR_CHAN_RED] = "red",
69 [SENSOR_CHAN_GREEN] = "green",
70 [SENSOR_CHAN_BLUE] = "blue",
71 [SENSOR_CHAN_ALTITUDE] = "altitude",
72 [SENSOR_CHAN_PM_1_0] = "pm_1_0",
73 [SENSOR_CHAN_PM_2_5] = "pm_2_5",
74 [SENSOR_CHAN_PM_10] = "pm_10",
75 [SENSOR_CHAN_DISTANCE] = "distance",
76 [SENSOR_CHAN_CO2] = "co2",
77 [SENSOR_CHAN_O2] = "o2",
78 [SENSOR_CHAN_VOC] = "voc",
79 [SENSOR_CHAN_GAS_RES] = "gas_resistance",
80 [SENSOR_CHAN_VOLTAGE] = "voltage",
81 [SENSOR_CHAN_VSHUNT] = "vshunt",
82 [SENSOR_CHAN_CURRENT] = "current",
83 [SENSOR_CHAN_POWER] = "power",
84 [SENSOR_CHAN_RESISTANCE] = "resistance",
85 [SENSOR_CHAN_ROTATION] = "rotation",
86 [SENSOR_CHAN_POS_DX] = "pos_dx",
87 [SENSOR_CHAN_POS_DY] = "pos_dy",
88 [SENSOR_CHAN_POS_DZ] = "pos_dz",
89 [SENSOR_CHAN_POS_DXYZ] = "pos_dxyz",
90 [SENSOR_CHAN_RPM] = "rpm",
91 [SENSOR_CHAN_GAUGE_VOLTAGE] = "gauge_voltage",
92 [SENSOR_CHAN_GAUGE_AVG_CURRENT] = "gauge_avg_current",
93 [SENSOR_CHAN_GAUGE_STDBY_CURRENT] = "gauge_stdby_current",
94 [SENSOR_CHAN_GAUGE_MAX_LOAD_CURRENT] = "gauge_max_load_current",
95 [SENSOR_CHAN_GAUGE_TEMP] = "gauge_temp",
96 [SENSOR_CHAN_GAUGE_STATE_OF_CHARGE] = "gauge_state_of_charge",
97 [SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY] = "gauge_full_cap",
98 [SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY] = "gauge_remaining_cap",
99 [SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY] = "gauge_nominal_cap",
100 [SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY] = "gauge_full_avail_cap",
101 [SENSOR_CHAN_GAUGE_AVG_POWER] = "gauge_avg_power",
102 [SENSOR_CHAN_GAUGE_STATE_OF_HEALTH] = "gauge_state_of_health",
103 [SENSOR_CHAN_GAUGE_TIME_TO_EMPTY] = "gauge_time_to_empty",
104 [SENSOR_CHAN_GAUGE_TIME_TO_FULL] = "gauge_time_to_full",
105 [SENSOR_CHAN_GAUGE_CYCLE_COUNT] = "gauge_cycle_count",
106 [SENSOR_CHAN_GAUGE_DESIGN_VOLTAGE] = "gauge_design_voltage",
107 [SENSOR_CHAN_GAUGE_DESIRED_VOLTAGE] = "gauge_desired_voltage",
108 [SENSOR_CHAN_GAUGE_DESIRED_CHARGING_CURRENT] = "gauge_desired_charging_current",
109 [SENSOR_CHAN_ALL] = "all",
110 };
111
112 static const char *sensor_attribute_name[SENSOR_ATTR_COMMON_COUNT] = {
113 [SENSOR_ATTR_SAMPLING_FREQUENCY] = "sampling_frequency",
114 [SENSOR_ATTR_LOWER_THRESH] = "lower_thresh",
115 [SENSOR_ATTR_UPPER_THRESH] = "upper_thresh",
116 [SENSOR_ATTR_SLOPE_TH] = "slope_th",
117 [SENSOR_ATTR_SLOPE_DUR] = "slope_dur",
118 [SENSOR_ATTR_HYSTERESIS] = "hysteresis",
119 [SENSOR_ATTR_OVERSAMPLING] = "oversampling",
120 [SENSOR_ATTR_FULL_SCALE] = "full_scale",
121 [SENSOR_ATTR_OFFSET] = "offset",
122 [SENSOR_ATTR_CALIB_TARGET] = "calib_target",
123 [SENSOR_ATTR_CONFIGURATION] = "configuration",
124 [SENSOR_ATTR_CALIBRATION] = "calibration",
125 [SENSOR_ATTR_FEATURE_MASK] = "feature_mask",
126 [SENSOR_ATTR_ALERT] = "alert",
127 [SENSOR_ATTR_FF_DUR] = "ff_dur",
128 [SENSOR_ATTR_BATCH_DURATION] = "batch_dur",
129 };
130
131 enum sample_stats_state {
132 SAMPLE_STATS_STATE_UNINITIALIZED = 0,
133 SAMPLE_STATS_STATE_ENABLED,
134 SAMPLE_STATS_STATE_DISABLED,
135 };
136
137 struct sample_stats {
138 int64_t accumulator;
139 uint64_t sample_window_start;
140 uint32_t count;
141 enum sample_stats_state state;
142 };
143
144 static struct sample_stats sensor_stats[CONFIG_SENSOR_SHELL_MAX_TRIGGER_DEVICES][SENSOR_CHAN_ALL];
145
146 static const struct device *sensor_trigger_devices[CONFIG_SENSOR_SHELL_MAX_TRIGGER_DEVICES];
147
device_is_sensor(const struct device * dev)148 static bool device_is_sensor(const struct device *dev)
149 {
150 #ifdef CONFIG_SENSOR_INFO
151 STRUCT_SECTION_FOREACH(sensor_info, sensor) {
152 if (sensor->dev == dev) {
153 return true;
154 }
155 }
156 return false;
157 #else
158 return true;
159 #endif /* CONFIG_SENSOR_INFO */
160 }
161
find_sensor_trigger_device(const struct device * sensor)162 static int find_sensor_trigger_device(const struct device *sensor)
163 {
164 for (int i = 0; i < CONFIG_SENSOR_SHELL_MAX_TRIGGER_DEVICES; i++) {
165 if (sensor_trigger_devices[i] == sensor) {
166 return i;
167 }
168 }
169 return -1;
170 }
171
sensor_device_check(const struct device * dev)172 static bool sensor_device_check(const struct device *dev)
173 {
174 return DEVICE_API_IS(sensor, dev);
175 }
176
177 /* Forward declaration */
178 static void data_ready_trigger_handler(const struct device *sensor,
179 const struct sensor_trigger *trigger);
180
181 #define TRIGGER_DATA_ENTRY(trig_enum, str_name, handler_func) \
182 [(trig_enum)] = {.name = #str_name, \
183 .handler = (handler_func), \
184 .trigger = {.chan = SENSOR_CHAN_ALL, .type = (trig_enum)}}
185
186 /**
187 * @brief This table stores a mapping of string trigger names along with the sensor_trigger struct
188 * that gets passed to the driver to enable that trigger, plus a function pointer to a handler. If
189 * that pointer is NULL, this indicates there is not currently support for that trigger type in the
190 * sensor shell.
191 */
192 static const struct {
193 const char *name;
194 sensor_trigger_handler_t handler;
195 struct sensor_trigger trigger;
196 } sensor_trigger_table[SENSOR_TRIG_COMMON_COUNT] = {
197 TRIGGER_DATA_ENTRY(SENSOR_TRIG_TIMER, timer, NULL),
198 TRIGGER_DATA_ENTRY(SENSOR_TRIG_DATA_READY, data_ready, data_ready_trigger_handler),
199 TRIGGER_DATA_ENTRY(SENSOR_TRIG_DELTA, delta, NULL),
200 TRIGGER_DATA_ENTRY(SENSOR_TRIG_NEAR_FAR, near_far, NULL),
201 TRIGGER_DATA_ENTRY(SENSOR_TRIG_THRESHOLD, threshold, NULL),
202 TRIGGER_DATA_ENTRY(SENSOR_TRIG_TAP, tap, NULL),
203 TRIGGER_DATA_ENTRY(SENSOR_TRIG_DOUBLE_TAP, double_tap, NULL),
204 TRIGGER_DATA_ENTRY(SENSOR_TRIG_FREEFALL, freefall, NULL),
205 TRIGGER_DATA_ENTRY(SENSOR_TRIG_MOTION, motion, NULL),
206 TRIGGER_DATA_ENTRY(SENSOR_TRIG_STATIONARY, stationary, NULL),
207 TRIGGER_DATA_ENTRY(SENSOR_TRIG_FIFO_WATERMARK, fifo_wm, NULL),
208 TRIGGER_DATA_ENTRY(SENSOR_TRIG_FIFO_FULL, fifo_full, NULL),
209 };
210
211 /**
212 * Lookup the sensor trigger data by name
213 *
214 * @param name The name of the trigger
215 * @return < 0 on error
216 * @return >= 0 if found
217 */
sensor_trigger_name_lookup(const char * name)218 static int sensor_trigger_name_lookup(const char *name)
219 {
220 for (int i = 0; i < ARRAY_SIZE(sensor_trigger_table); ++i) {
221 if (strcmp(name, sensor_trigger_table[i].name) == 0) {
222 return i;
223 }
224 }
225 return -1;
226 }
227
228 enum dynamic_command_context {
229 NONE,
230 CTX_GET,
231 CTX_ATTR_GET_SET,
232 CTX_STREAM_ON_OFF,
233 };
234
235 static enum dynamic_command_context current_cmd_ctx = NONE;
236
237 /* Mutex for accessing shared RTIO/IODEV data structures */
238 K_MUTEX_DEFINE(cmd_get_mutex);
239
240 /* Crate a single common config for one-shot reading */
241 static struct sensor_chan_spec iodev_sensor_shell_channels[SENSOR_CHAN_ALL];
242 static struct sensor_read_config iodev_sensor_shell_read_config = {
243 .sensor = NULL,
244 .is_streaming = false,
245 .channels = iodev_sensor_shell_channels,
246 .count = 0,
247 .max = ARRAY_SIZE(iodev_sensor_shell_channels),
248 };
249 RTIO_IODEV_DEFINE(iodev_sensor_shell_read, &__sensor_iodev_api, &iodev_sensor_shell_read_config);
250
251 /* Create the RTIO context to service the reading */
252 RTIO_DEFINE_WITH_MEMPOOL(sensor_read_rtio, 8, 8, 32, 64, 4);
253
parse_named_int(const char * name,const char * heystack[],size_t count)254 static int parse_named_int(const char *name, const char *heystack[], size_t count)
255 {
256 char *endptr;
257 int i;
258
259 /* Attempt to parse channel name as a number first */
260 i = strtoul(name, &endptr, 0);
261
262 if (*endptr == '\0') {
263 return i;
264 }
265
266 /* Channel name is not a number, look it up */
267 for (i = 0; i < count; i++) {
268 if (strcmp(name, heystack[i]) == 0) {
269 return i;
270 }
271 }
272
273 return -ENOTSUP;
274 }
275
parse_sensor_value(const char * val_str,struct sensor_value * out)276 static int parse_sensor_value(const char *val_str, struct sensor_value *out)
277 {
278 const bool is_negative = val_str[0] == '-';
279 const char *decimal_pos = strchr(val_str, '.');
280 long value;
281 char *endptr;
282
283 /* Parse int portion */
284 value = strtol(val_str, &endptr, 0);
285
286 if (*endptr != '\0' && *endptr != '.') {
287 return -EINVAL;
288 }
289 if (value > INT32_MAX || value < INT32_MIN) {
290 return -EINVAL;
291 }
292 out->val1 = (int32_t)value;
293
294 if (decimal_pos == NULL) {
295 return 0;
296 }
297
298 /* Parse the decimal portion */
299 value = strtoul(decimal_pos + 1, &endptr, 0);
300 if (*endptr != '\0') {
301 return -EINVAL;
302 }
303 while (value < 100000) {
304 value *= 10;
305 }
306 if (value > INT32_C(999999)) {
307 return -EINVAL;
308 }
309 out->val2 = (int32_t)value;
310 if (is_negative) {
311 out->val2 *= -1;
312 }
313 return 0;
314 }
315
sensor_shell_processing_callback(int result,uint8_t * buf,uint32_t buf_len,void * userdata)316 void sensor_shell_processing_callback(int result, uint8_t *buf, uint32_t buf_len, void *userdata)
317 {
318 struct sensor_shell_processing_context *ctx = userdata;
319 const struct sensor_decoder_api *decoder;
320 uint8_t decoded_buffer[128];
321 struct {
322 uint64_t base_timestamp_ns;
323 int count;
324 uint64_t timestamp_delta;
325 int64_t values[3];
326 int8_t shift;
327 } accumulator_buffer;
328 int rc;
329
330 ARG_UNUSED(buf_len);
331
332 if (result < 0) {
333 shell_error(ctx->sh, "Read failed");
334 return;
335 }
336
337 rc = sensor_get_decoder(ctx->dev, &decoder);
338 if (rc != 0) {
339 shell_error(ctx->sh, "Failed to get decoder for '%s'", ctx->dev->name);
340 return;
341 }
342
343 for (int trigger = 0; decoder->has_trigger != NULL && trigger < SENSOR_TRIG_COMMON_COUNT;
344 ++trigger) {
345 if (!decoder->has_trigger(buf, trigger)) {
346 continue;
347 }
348 shell_info(ctx->sh, "Trigger (%d / %s) detected", trigger,
349 (sensor_trigger_table[trigger].name == NULL
350 ? "UNKNOWN"
351 : sensor_trigger_table[trigger].name));
352 }
353
354
355
356 for (struct sensor_chan_spec ch = {0, 0}; ch.chan_type < SENSOR_CHAN_ALL; ch.chan_type++) {
357 uint32_t fit = 0;
358 size_t base_size;
359 size_t frame_size;
360 uint16_t frame_count;
361
362 /* Channels with multi-axis equivalents are skipped */
363 switch (ch.chan_type) {
364 case SENSOR_CHAN_ACCEL_X:
365 case SENSOR_CHAN_ACCEL_Y:
366 case SENSOR_CHAN_ACCEL_Z:
367 case SENSOR_CHAN_GYRO_X:
368 case SENSOR_CHAN_GYRO_Y:
369 case SENSOR_CHAN_GYRO_Z:
370 case SENSOR_CHAN_MAGN_X:
371 case SENSOR_CHAN_MAGN_Y:
372 case SENSOR_CHAN_MAGN_Z:
373 case SENSOR_CHAN_POS_DX:
374 case SENSOR_CHAN_POS_DY:
375 case SENSOR_CHAN_POS_DZ:
376 continue;
377 }
378
379 rc = decoder->get_size_info(ch, &base_size, &frame_size);
380 if (rc != 0) {
381 LOG_DBG("skipping unsupported channel %s:%d",
382 sensor_channel_name[ch.chan_type], ch.chan_idx);
383 /* Channel not supported, skipping */
384 continue;
385 }
386
387 if (base_size > ARRAY_SIZE(decoded_buffer)) {
388 shell_error(ctx->sh,
389 "Channel (type %d, idx %d) requires %zu bytes to decode, but "
390 "only %zu are available",
391 ch.chan_type, ch.chan_idx, base_size,
392 ARRAY_SIZE(decoded_buffer));
393 continue;
394 }
395
396 while (decoder->get_frame_count(buf, ch, &frame_count) == 0) {
397 LOG_DBG("decoding %d frames from channel %s:%d",
398 frame_count, sensor_channel_name[ch.chan_type], ch.chan_idx);
399 fit = 0;
400 memset(&accumulator_buffer, 0, sizeof(accumulator_buffer));
401 while (decoder->decode(buf, ch, &fit, 1, decoded_buffer) > 0) {
402 switch (ch.chan_type) {
403 case SENSOR_CHAN_ACCEL_XYZ:
404 case SENSOR_CHAN_GYRO_XYZ:
405 case SENSOR_CHAN_MAGN_XYZ:
406 case SENSOR_CHAN_POS_DXYZ: {
407 struct sensor_three_axis_data *data =
408 (struct sensor_three_axis_data *)decoded_buffer;
409
410 if (accumulator_buffer.count == 0) {
411 accumulator_buffer.base_timestamp_ns =
412 data->header.base_timestamp_ns;
413 }
414 accumulator_buffer.count++;
415 accumulator_buffer.shift = data->shift;
416 accumulator_buffer.timestamp_delta +=
417 data->readings[0].timestamp_delta;
418 accumulator_buffer.values[0] += data->readings[0].values[0];
419 accumulator_buffer.values[1] += data->readings[0].values[1];
420 accumulator_buffer.values[2] += data->readings[0].values[2];
421 break;
422 }
423 case SENSOR_CHAN_PROX: {
424 struct sensor_byte_data *data =
425 (struct sensor_byte_data *)decoded_buffer;
426
427 if (accumulator_buffer.count == 0) {
428 accumulator_buffer.base_timestamp_ns =
429 data->header.base_timestamp_ns;
430 }
431 accumulator_buffer.count++;
432 accumulator_buffer.timestamp_delta +=
433 data->readings[0].timestamp_delta;
434 accumulator_buffer.values[0] += data->readings[0].is_near;
435 break;
436 }
437 default: {
438 struct sensor_q31_data *data =
439 (struct sensor_q31_data *)decoded_buffer;
440
441 if (accumulator_buffer.count == 0) {
442 accumulator_buffer.base_timestamp_ns =
443 data->header.base_timestamp_ns;
444 }
445 accumulator_buffer.count++;
446 accumulator_buffer.shift = data->shift;
447 accumulator_buffer.timestamp_delta +=
448 data->readings[0].timestamp_delta;
449 accumulator_buffer.values[0] += data->readings[0].value;
450 break;
451 }
452 }
453 }
454
455 /* Print the accumulated value average */
456 switch (ch.chan_type) {
457 case SENSOR_CHAN_ACCEL_XYZ:
458 case SENSOR_CHAN_GYRO_XYZ:
459 case SENSOR_CHAN_MAGN_XYZ:
460 case SENSOR_CHAN_POS_DXYZ: {
461 struct sensor_three_axis_data *data =
462 (struct sensor_three_axis_data *)decoded_buffer;
463
464 data->header.base_timestamp_ns =
465 accumulator_buffer.base_timestamp_ns;
466 data->header.reading_count = 1;
467 data->shift = accumulator_buffer.shift;
468 data->readings[0].timestamp_delta =
469 (uint32_t)(accumulator_buffer.timestamp_delta /
470 accumulator_buffer.count);
471 data->readings[0].values[0] = (q31_t)(accumulator_buffer.values[0] /
472 accumulator_buffer.count);
473 data->readings[0].values[1] = (q31_t)(accumulator_buffer.values[1] /
474 accumulator_buffer.count);
475 data->readings[0].values[2] = (q31_t)(accumulator_buffer.values[2] /
476 accumulator_buffer.count);
477 shell_info(ctx->sh,
478 "channel type=%d(%s) index=%d shift=%d num_samples=%d "
479 "value=%" PRIsensor_three_axis_data,
480 ch.chan_type, sensor_channel_name[ch.chan_type],
481 ch.chan_idx, data->shift, accumulator_buffer.count,
482 PRIsensor_three_axis_data_arg(*data, 0));
483 break;
484 }
485 case SENSOR_CHAN_PROX: {
486 struct sensor_byte_data *data =
487 (struct sensor_byte_data *)decoded_buffer;
488
489 data->header.base_timestamp_ns =
490 accumulator_buffer.base_timestamp_ns;
491 data->header.reading_count = 1;
492 data->readings[0].timestamp_delta =
493 (uint32_t)(accumulator_buffer.timestamp_delta /
494 accumulator_buffer.count);
495 data->readings[0].is_near =
496 accumulator_buffer.values[0] / accumulator_buffer.count;
497
498 shell_info(ctx->sh,
499 "channel type=%d(%s) index=%d num_samples=%d "
500 "value=%" PRIsensor_byte_data(is_near),
501 ch.chan_type, sensor_channel_name[ch.chan_type],
502 ch.chan_idx, accumulator_buffer.count,
503 PRIsensor_byte_data_arg(*data, 0, is_near));
504 break;
505 }
506 default: {
507 struct sensor_q31_data *data =
508 (struct sensor_q31_data *)decoded_buffer;
509
510 data->header.base_timestamp_ns =
511 accumulator_buffer.base_timestamp_ns;
512 data->header.reading_count = 1;
513 data->shift = accumulator_buffer.shift;
514 data->readings[0].timestamp_delta =
515 (uint32_t)(accumulator_buffer.timestamp_delta /
516 accumulator_buffer.count);
517 data->readings[0].value = (q31_t)(accumulator_buffer.values[0] /
518 accumulator_buffer.count);
519
520 shell_info(ctx->sh,
521 "channel type=%d(%s) index=%d shift=%d num_samples=%d "
522 "value=%" PRIsensor_q31_data,
523 ch.chan_type,
524 (ch.chan_type >= ARRAY_SIZE(sensor_channel_name))
525 ? ""
526 : sensor_channel_name[ch.chan_type],
527 ch.chan_idx,
528 data->shift, accumulator_buffer.count,
529 PRIsensor_q31_data_arg(*data, 0));
530 }
531 }
532 ++ch.chan_idx;
533 }
534 ch.chan_idx = 0;
535 }
536 }
537
cmd_get_sensor(const struct shell * sh,size_t argc,char * argv[])538 static int cmd_get_sensor(const struct shell *sh, size_t argc, char *argv[])
539 {
540 static struct sensor_shell_processing_context ctx;
541 const struct device *dev;
542 int count = 0;
543 int err;
544
545 err = k_mutex_lock(&cmd_get_mutex, K_NO_WAIT);
546 if (err < 0) {
547 shell_error(sh, "Another sensor reading in progress");
548 return err;
549 }
550
551 dev = shell_device_get_binding(argv[1]);
552 if (dev == NULL || !sensor_device_check(dev)) {
553 shell_error(sh, "Sensor device unknown (%s)", argv[1]);
554 k_mutex_unlock(&cmd_get_mutex);
555 return -ENODEV;
556 }
557
558 if (!device_is_sensor(dev)) {
559 shell_error(sh, "Device is not a sensor (%s)", argv[1]);
560 k_mutex_unlock(&cmd_get_mutex);
561 return -ENODEV;
562 }
563
564 if (argc == 2) {
565 /* read all channel types */
566 for (int i = 0; i < ARRAY_SIZE(iodev_sensor_shell_channels); ++i) {
567 if (SENSOR_CHANNEL_3_AXIS(i)) {
568 continue;
569 }
570 iodev_sensor_shell_channels[count++] = (struct sensor_chan_spec){i, 0};
571 }
572 } else {
573 /* read specific channels */
574 for (int i = 2; i < argc; ++i) {
575 int chan = parse_named_int(argv[i], sensor_channel_name,
576 ARRAY_SIZE(sensor_channel_name));
577
578 if (chan < 0) {
579 shell_error(sh, "Failed to read channel (%s)", argv[i]);
580 continue;
581 }
582 iodev_sensor_shell_channels[count++] =
583 (struct sensor_chan_spec){chan, 0};
584 }
585 }
586
587 if (count == 0) {
588 shell_error(sh, "No channels to read, bailing");
589 k_mutex_unlock(&cmd_get_mutex);
590 return -EINVAL;
591 }
592 iodev_sensor_shell_read_config.sensor = dev;
593 iodev_sensor_shell_read_config.count = count;
594
595 ctx.dev = dev;
596 ctx.sh = sh;
597 err = sensor_read_async_mempool(&iodev_sensor_shell_read, &sensor_read_rtio, &ctx);
598 if (err < 0) {
599 shell_error(sh, "Failed to read sensor: %d", err);
600 }
601 if (!IS_ENABLED(CONFIG_SENSOR_SHELL_STREAM)) {
602 /*
603 * Streaming enables a thread that polls the RTIO context, so if it's enabled, we
604 * don't need a blocking read here.
605 */
606 sensor_processing_with_callback(&sensor_read_rtio,
607 sensor_shell_processing_callback);
608 }
609
610 k_mutex_unlock(&cmd_get_mutex);
611
612 return 0;
613 }
614
cmd_sensor_attr_set(const struct shell * shell_ptr,size_t argc,char * argv[])615 static int cmd_sensor_attr_set(const struct shell *shell_ptr, size_t argc, char *argv[])
616 {
617 const struct device *dev;
618 int rc;
619
620 dev = shell_device_get_binding(argv[1]);
621 if (dev == NULL || !sensor_device_check(dev)) {
622 shell_error(shell_ptr, "Sensor device unknown (%s)", argv[1]);
623 return -ENODEV;
624 }
625
626 if (!device_is_sensor(dev)) {
627 shell_error(shell_ptr, "Device is not a sensor (%s)", argv[1]);
628 k_mutex_unlock(&cmd_get_mutex);
629 return -ENODEV;
630 }
631
632 for (size_t i = 2; i < argc; i += 3) {
633 int channel = parse_named_int(argv[i], sensor_channel_name,
634 ARRAY_SIZE(sensor_channel_name));
635 int attr = parse_named_int(argv[i + 1], sensor_attribute_name,
636 ARRAY_SIZE(sensor_attribute_name));
637 struct sensor_value value = {0};
638
639 if (channel < 0) {
640 shell_error(shell_ptr, "Channel '%s' unknown", argv[i]);
641 return -EINVAL;
642 }
643 if (attr < 0) {
644 shell_error(shell_ptr, "Attribute '%s' unknown", argv[i + 1]);
645 return -EINVAL;
646 }
647 if (parse_sensor_value(argv[i + 2], &value)) {
648 shell_error(shell_ptr, "Sensor value '%s' invalid", argv[i + 2]);
649 return -EINVAL;
650 }
651
652 rc = sensor_attr_set(dev, channel, attr, &value);
653 if (rc) {
654 shell_error(shell_ptr, "Failed to set channel(%s) attribute(%s): %d",
655 sensor_channel_name[channel], sensor_attribute_name[attr], rc);
656 continue;
657 }
658 shell_info(shell_ptr, "%s channel=%s, attr=%s set to value=%s", dev->name,
659 sensor_channel_name[channel], sensor_attribute_name[attr], argv[i + 2]);
660 }
661 return 0;
662 }
663
cmd_sensor_attr_get_handler(const struct shell * shell_ptr,const struct device * dev,const char * channel_name,const char * attr_name,bool print_missing_attribute)664 static void cmd_sensor_attr_get_handler(const struct shell *shell_ptr, const struct device *dev,
665 const char *channel_name, const char *attr_name,
666 bool print_missing_attribute)
667 {
668 int channel =
669 parse_named_int(channel_name, sensor_channel_name, ARRAY_SIZE(sensor_channel_name));
670 int attr = parse_named_int(attr_name, sensor_attribute_name,
671 ARRAY_SIZE(sensor_attribute_name));
672 struct sensor_value value = {0};
673 int rc;
674
675 if (channel < 0) {
676 shell_error(shell_ptr, "Channel '%s' unknown", channel_name);
677 return;
678 }
679 if (attr < 0) {
680 shell_error(shell_ptr, "Attribute '%s' unknown", attr_name);
681 return;
682 }
683
684 rc = sensor_attr_get(dev, channel, attr, &value);
685
686 if (rc != 0) {
687 if (rc == -EINVAL && !print_missing_attribute) {
688 return;
689 }
690 shell_error(shell_ptr, "Failed to get channel(%s) attribute(%s): %d",
691 sensor_channel_name[channel], sensor_attribute_name[attr], rc);
692 return;
693 }
694
695 shell_info(shell_ptr, "%s(channel=%s, attr=%s) value=%.6f", dev->name,
696 sensor_channel_name[channel], sensor_attribute_name[attr],
697 sensor_value_to_double(&value));
698 }
699
cmd_sensor_attr_get(const struct shell * shell_ptr,size_t argc,char * argv[])700 static int cmd_sensor_attr_get(const struct shell *shell_ptr, size_t argc, char *argv[])
701 {
702 const struct device *dev;
703
704 dev = shell_device_get_binding(argv[1]);
705 if (dev == NULL || !sensor_device_check(dev)) {
706 shell_error(shell_ptr, "Sensor device unknown (%s)", argv[1]);
707 return -ENODEV;
708 }
709
710 if (!device_is_sensor(dev)) {
711 shell_error(shell_ptr, "Device is not a sensor (%s)", argv[1]);
712 k_mutex_unlock(&cmd_get_mutex);
713 return -ENODEV;
714 }
715
716 if (argc > 2) {
717 for (size_t i = 2; i < argc; i += 2) {
718 cmd_sensor_attr_get_handler(shell_ptr, dev, argv[i], argv[i + 1],
719 /*print_missing_attribute=*/true);
720 }
721 } else {
722 for (size_t channel_idx = 0; channel_idx < ARRAY_SIZE(sensor_channel_name);
723 ++channel_idx) {
724 for (size_t attr_idx = 0; attr_idx < ARRAY_SIZE(sensor_attribute_name);
725 ++attr_idx) {
726 cmd_sensor_attr_get_handler(shell_ptr, dev,
727 sensor_channel_name[channel_idx],
728 sensor_attribute_name[attr_idx],
729 /*print_missing_attribute=*/false);
730 }
731 }
732 }
733 return 0;
734 }
735
736 static void channel_name_get(size_t idx, struct shell_static_entry *entry);
737 SHELL_DYNAMIC_CMD_CREATE(dsub_channel_name, channel_name_get);
738
739 static void attribute_name_get(size_t idx, struct shell_static_entry *entry);
740 SHELL_DYNAMIC_CMD_CREATE(dsub_attribute_name, attribute_name_get);
741
channel_name_get(size_t idx,struct shell_static_entry * entry)742 static void channel_name_get(size_t idx, struct shell_static_entry *entry)
743 {
744 int cnt = 0;
745
746 entry->syntax = NULL;
747 entry->handler = NULL;
748 entry->help = NULL;
749 if (current_cmd_ctx == CTX_GET) {
750 entry->subcmd = &dsub_channel_name;
751 } else if (current_cmd_ctx == CTX_ATTR_GET_SET) {
752 entry->subcmd = &dsub_attribute_name;
753 } else {
754 entry->subcmd = NULL;
755 }
756
757 for (int i = 0; i < ARRAY_SIZE(sensor_channel_name); i++) {
758 if (sensor_channel_name[i] != NULL) {
759 if (cnt == idx) {
760 entry->syntax = sensor_channel_name[i];
761 break;
762 }
763 cnt++;
764 }
765 }
766 }
767
attribute_name_get(size_t idx,struct shell_static_entry * entry)768 static void attribute_name_get(size_t idx, struct shell_static_entry *entry)
769 {
770 int cnt = 0;
771
772 entry->syntax = NULL;
773 entry->handler = NULL;
774 entry->help = NULL;
775 entry->subcmd = &dsub_channel_name;
776
777 for (int i = 0; i < ARRAY_SIZE(sensor_attribute_name); i++) {
778 if (sensor_attribute_name[i] != NULL) {
779 if (cnt == idx) {
780 entry->syntax = sensor_attribute_name[i];
781 break;
782 }
783 cnt++;
784 }
785 }
786 }
787
788 static void trigger_opt_get_for_stream(size_t idx, struct shell_static_entry *entry);
789 SHELL_DYNAMIC_CMD_CREATE(dsub_trigger_opt_get_for_stream, trigger_opt_get_for_stream);
790
trigger_opt_get_for_stream(size_t idx,struct shell_static_entry * entry)791 static void trigger_opt_get_for_stream(size_t idx, struct shell_static_entry *entry)
792 {
793 entry->syntax = NULL;
794 entry->handler = NULL;
795 entry->help = NULL;
796 entry->subcmd = NULL;
797
798 switch (idx) {
799 case SENSOR_STREAM_DATA_INCLUDE:
800 entry->syntax = "incl";
801 break;
802 case SENSOR_STREAM_DATA_DROP:
803 entry->syntax = "drop";
804 break;
805 case SENSOR_STREAM_DATA_NOP:
806 entry->syntax = "nop";
807 break;
808 }
809 }
810
811 static void trigger_name_get_for_stream(size_t idx, struct shell_static_entry *entry);
812 SHELL_DYNAMIC_CMD_CREATE(dsub_trigger_name_for_stream, trigger_name_get_for_stream);
813
trigger_name_get_for_stream(size_t idx,struct shell_static_entry * entry)814 static void trigger_name_get_for_stream(size_t idx, struct shell_static_entry *entry)
815 {
816 int cnt = 0;
817
818 entry->syntax = NULL;
819 entry->handler = NULL;
820 entry->help = NULL;
821 entry->subcmd = &dsub_trigger_opt_get_for_stream;
822
823 for (int i = 0; i < ARRAY_SIZE(sensor_trigger_table); i++) {
824 if (sensor_trigger_table[i].name != NULL) {
825 if (cnt == idx) {
826 entry->syntax = sensor_trigger_table[i].name;
827 break;
828 }
829 cnt++;
830 }
831 }
832 }
833
stream_on_off(size_t idx,struct shell_static_entry * entry)834 static void stream_on_off(size_t idx, struct shell_static_entry *entry)
835 {
836 entry->syntax = NULL;
837 entry->handler = NULL;
838 entry->help = NULL;
839
840 if (idx == 0) {
841 entry->syntax = "on";
842 entry->subcmd = &dsub_trigger_name_for_stream;
843 } else if (idx == 1) {
844 entry->syntax = "off";
845 entry->subcmd = NULL;
846 }
847 }
848 SHELL_DYNAMIC_CMD_CREATE(dsub_stream_on_off, stream_on_off);
849
850 static void device_name_get(size_t idx, struct shell_static_entry *entry);
851
852 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
853
device_name_get(size_t idx,struct shell_static_entry * entry)854 static void device_name_get(size_t idx, struct shell_static_entry *entry)
855 {
856 const struct device *dev = shell_device_filter(idx, sensor_device_check);
857
858 current_cmd_ctx = CTX_GET;
859 entry->syntax = (dev != NULL) ? dev->name : NULL;
860 entry->handler = NULL;
861 entry->help = NULL;
862 entry->subcmd = &dsub_channel_name;
863 }
864
device_name_get_for_attr(size_t idx,struct shell_static_entry * entry)865 static void device_name_get_for_attr(size_t idx, struct shell_static_entry *entry)
866 {
867 const struct device *dev = shell_device_lookup(idx, NULL);
868
869 current_cmd_ctx = CTX_ATTR_GET_SET;
870 entry->syntax = (dev != NULL) ? dev->name : NULL;
871 entry->handler = NULL;
872 entry->help = NULL;
873 entry->subcmd = &dsub_channel_name;
874 }
875 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name_for_attr, device_name_get_for_attr);
876
trigger_name_get(size_t idx,struct shell_static_entry * entry)877 static void trigger_name_get(size_t idx, struct shell_static_entry *entry)
878 {
879 int cnt = 0;
880
881 entry->syntax = NULL;
882 entry->handler = NULL;
883 entry->help = NULL;
884 entry->subcmd = NULL;
885
886 for (int i = 0; i < ARRAY_SIZE(sensor_trigger_table); i++) {
887 if (sensor_trigger_table[i].name != NULL) {
888 if (cnt == idx) {
889 entry->syntax = sensor_trigger_table[i].name;
890 break;
891 }
892 cnt++;
893 }
894 }
895 }
896
897 SHELL_DYNAMIC_CMD_CREATE(dsub_trigger_name, trigger_name_get);
898
trigger_on_off_get(size_t idx,struct shell_static_entry * entry)899 static void trigger_on_off_get(size_t idx, struct shell_static_entry *entry)
900 {
901 entry->handler = NULL;
902 entry->help = NULL;
903 entry->subcmd = &dsub_trigger_name;
904
905 switch (idx) {
906 case 0:
907 entry->syntax = "on";
908 break;
909 case 1:
910 entry->syntax = "off";
911 break;
912 default:
913 entry->syntax = NULL;
914 break;
915 }
916 }
917
918 SHELL_DYNAMIC_CMD_CREATE(dsub_trigger_onoff, trigger_on_off_get);
919
device_name_get_for_trigger(size_t idx,struct shell_static_entry * entry)920 static void device_name_get_for_trigger(size_t idx, struct shell_static_entry *entry)
921 {
922 const struct device *dev = shell_device_lookup(idx, NULL);
923
924 entry->syntax = (dev != NULL) ? dev->name : NULL;
925 entry->handler = NULL;
926 entry->help = NULL;
927 entry->subcmd = &dsub_trigger_onoff;
928 }
929
930 SHELL_DYNAMIC_CMD_CREATE(dsub_trigger, device_name_get_for_trigger);
931
device_name_get_for_stream(size_t idx,struct shell_static_entry * entry)932 static void device_name_get_for_stream(size_t idx, struct shell_static_entry *entry)
933 {
934 const struct device *dev = shell_device_lookup(idx, NULL);
935
936 current_cmd_ctx = CTX_STREAM_ON_OFF;
937 entry->syntax = (dev != NULL) ? dev->name : NULL;
938 entry->handler = NULL;
939 entry->help = NULL;
940 entry->subcmd = &dsub_stream_on_off;
941 }
942 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name_for_stream, device_name_get_for_stream);
943
cmd_get_sensor_info(const struct shell * sh,size_t argc,char ** argv)944 static int cmd_get_sensor_info(const struct shell *sh, size_t argc, char **argv)
945 {
946 ARG_UNUSED(argc);
947 ARG_UNUSED(argv);
948
949 #ifdef CONFIG_SENSOR_INFO
950 const char *null_str = "(null)";
951
952 STRUCT_SECTION_FOREACH(sensor_info, sensor) {
953 shell_print(sh,
954 "device name: %s, vendor: %s, model: %s, "
955 "friendly name: %s",
956 sensor->dev->name, sensor->vendor ? sensor->vendor : null_str,
957 sensor->model ? sensor->model : null_str,
958 sensor->friendly_name ? sensor->friendly_name : null_str);
959 }
960 return 0;
961 #else
962 return -EINVAL;
963 #endif
964 }
965
data_ready_trigger_handler(const struct device * sensor,const struct sensor_trigger * trigger)966 static void data_ready_trigger_handler(const struct device *sensor,
967 const struct sensor_trigger *trigger)
968 {
969 const int64_t now = k_uptime_get();
970 struct sensor_value value;
971 int sensor_idx = find_sensor_trigger_device(sensor);
972 struct sample_stats *stats;
973 int sensor_name_len_before_at;
974 const char *sensor_name;
975
976 if (sensor_idx < 0) {
977 LOG_ERR("Unable to find sensor trigger device");
978 return;
979 }
980 stats = sensor_stats[sensor_idx];
981 sensor_name = sensor_trigger_devices[sensor_idx]->name;
982 if (sensor_name) {
983 sensor_name_len_before_at = strchr(sensor_name, '@') - sensor_name;
984 } else {
985 sensor_name_len_before_at = 0;
986 }
987
988 if (sensor_sample_fetch(sensor)) {
989 LOG_ERR("Failed to fetch samples on data ready handler");
990 }
991 for (int i = 0; i < SENSOR_CHAN_ALL; ++i) {
992 int rc;
993
994 /* Skip disabled channels */
995 if (stats[i].state == SAMPLE_STATS_STATE_DISABLED) {
996 continue;
997 }
998 /* Skip 3 axis channels */
999 if (SENSOR_CHANNEL_3_AXIS(i)) {
1000 continue;
1001 }
1002
1003 rc = sensor_channel_get(sensor, i, &value);
1004 if (stats[i].state == SAMPLE_STATS_STATE_UNINITIALIZED) {
1005 if (rc == -ENOTSUP) {
1006 /*
1007 * Stop reading this channel if the driver told us
1008 * it's not supported.
1009 */
1010 stats[i].state = SAMPLE_STATS_STATE_DISABLED;
1011 } else if (rc == 0) {
1012 stats[i].state = SAMPLE_STATS_STATE_ENABLED;
1013 }
1014 }
1015 if (rc != 0) {
1016 /* Skip on any error. */
1017 continue;
1018 }
1019 /* Do something with the data */
1020 stats[i].accumulator += value.val1 * INT64_C(1000000) + value.val2;
1021 if (stats[i].count++ == 0) {
1022 stats[i].sample_window_start = now;
1023 } else if (now > stats[i].sample_window_start +
1024 CONFIG_SENSOR_SHELL_TRIG_PRINT_TIMEOUT_MS) {
1025 int64_t micro_value = stats[i].accumulator / stats[i].count;
1026
1027 value.val1 = micro_value / 1000000;
1028 value.val2 = (int32_t)llabs(micro_value - (value.val1 * 1000000));
1029 LOG_INF("sensor=%.*s, chan=%s, num_samples=%u, data=%d.%06d",
1030 sensor_name_len_before_at, sensor_name,
1031 sensor_channel_name[i],
1032 stats[i].count,
1033 value.val1, value.val2);
1034
1035 stats[i].accumulator = 0;
1036 stats[i].count = 0;
1037 }
1038 }
1039 }
1040
cmd_trig_sensor(const struct shell * sh,size_t argc,char ** argv)1041 static int cmd_trig_sensor(const struct shell *sh, size_t argc, char **argv)
1042 {
1043 const struct device *dev;
1044 int trigger;
1045 bool trigger_enabled = false;
1046 int err;
1047
1048 if (argc < 4) {
1049 shell_error(sh, "Wrong number of args");
1050 return -EINVAL;
1051 }
1052
1053 /* Parse device name */
1054 dev = shell_device_get_binding(argv[1]);
1055 if (dev == NULL || !sensor_device_check(dev)) {
1056 shell_error(sh, "Sensor device unknown (%s)", argv[1]);
1057 return -ENODEV;
1058 }
1059
1060 /* Map the trigger string to an enum value */
1061 trigger = sensor_trigger_name_lookup(argv[3]);
1062 if (trigger < 0 || sensor_trigger_table[trigger].handler == NULL) {
1063 shell_error(sh, "Unsupported trigger type (%s)", argv[3]);
1064 return -ENOTSUP;
1065 }
1066
1067 /* Parse on/off */
1068 if (strcmp(argv[2], "on") == 0) {
1069 /* find a free entry in sensor_trigger_devices[] */
1070 int sensor_idx = find_sensor_trigger_device(NULL);
1071
1072 if (sensor_idx < 0) {
1073 shell_error(sh, "Unable to support more simultaneous sensor trigger"
1074 " devices");
1075 err = -ENOTSUP;
1076 } else {
1077 struct sample_stats *stats = sensor_stats[sensor_idx];
1078
1079 sensor_trigger_devices[sensor_idx] = dev;
1080 /* reset stats state to UNINITIALIZED */
1081 for (unsigned int ch = 0; ch < SENSOR_CHAN_ALL; ch++) {
1082 stats[ch].state = SAMPLE_STATS_STATE_UNINITIALIZED;
1083 }
1084 err = sensor_trigger_set(dev, &sensor_trigger_table[trigger].trigger,
1085 sensor_trigger_table[trigger].handler);
1086 trigger_enabled = true;
1087 }
1088 } else if (strcmp(argv[2], "off") == 0) {
1089 /* Clear the handler for the given trigger on this device */
1090 err = sensor_trigger_set(dev, &sensor_trigger_table[trigger].trigger, NULL);
1091 if (!err) {
1092 /* find entry in sensor_trigger_devices[] and free it */
1093 int sensor_idx = find_sensor_trigger_device(dev);
1094
1095 if (sensor_idx < 0) {
1096 shell_error(sh, "Unable to find sensor device in trigger array");
1097 } else {
1098 sensor_trigger_devices[sensor_idx] = NULL;
1099 }
1100 }
1101 } else {
1102 shell_error(sh, "Pass 'on' or 'off' to enable/disable trigger");
1103 return -EINVAL;
1104 }
1105
1106 if (err) {
1107 shell_error(sh, "Error while setting trigger %d on device %s (%d)", trigger,
1108 argv[1], err);
1109 } else {
1110 shell_info(sh, "%s trigger idx=%d %s on device %s",
1111 trigger_enabled ? "Enabled" : "Disabled", trigger,
1112 sensor_trigger_table[trigger].name, argv[1]);
1113 }
1114
1115 return err;
1116 }
1117
1118 /* clang-format off */
1119 SHELL_STATIC_SUBCMD_SET_CREATE(sub_sensor,
1120 SHELL_CMD_ARG(get, &dsub_device_name, SENSOR_GET_HELP, cmd_get_sensor,
1121 2, 255),
1122 SHELL_CMD_ARG(attr_set, &dsub_device_name_for_attr, SENSOR_ATTR_SET_HELP,
1123 cmd_sensor_attr_set, 2, 255),
1124 SHELL_CMD_ARG(attr_get, &dsub_device_name_for_attr, SENSOR_ATTR_GET_HELP,
1125 cmd_sensor_attr_get, 2, 255),
1126 SHELL_COND_CMD(CONFIG_SENSOR_SHELL_STREAM, stream, &dsub_device_name_for_stream,
1127 SENSOR_STREAM_HELP, cmd_sensor_stream),
1128 SHELL_COND_CMD(CONFIG_SENSOR_INFO, info, NULL, SENSOR_INFO_HELP,
1129 cmd_get_sensor_info),
1130 SHELL_CMD_ARG(trig, &dsub_trigger, SENSOR_TRIG_HELP, cmd_trig_sensor,
1131 2, 255),
1132 SHELL_SUBCMD_SET_END
1133 );
1134 /* clang-format on */
1135
1136 SHELL_CMD_REGISTER(sensor, &sub_sensor, "Sensor commands", NULL);
1137