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 LOG_MODULE_REGISTER(sensor_shell);
20
21 #define SENSOR_GET_HELP \
22 "Get sensor data. Channel names are optional. All channels are read " \
23 "when no channels are provided. Syntax:\n" \
24 "<device_name> <channel name 0> .. <channel name N>"
25
26 #define SENSOR_ATTR_GET_HELP \
27 "Get the sensor's channel attribute. Syntax:\n" \
28 "<device_name> [<channel_name 0> <attribute_name 0> .. " \
29 "<channel_name N> <attribute_name N>]"
30
31 #define SENSOR_ATTR_SET_HELP \
32 "Set the sensor's channel attribute.\n" \
33 "<device_name> <channel_name> <attribute_name> <value>"
34
35 #define SENSOR_INFO_HELP "Get sensor info, such as vendor and model name, for all sensors."
36
37 #define SENSOR_TRIG_HELP \
38 "Get or set the trigger type on a sensor. Currently only supports `data_ready`.\n" \
39 "<device_name> <on/off> <trigger_name>"
40
41 const char *sensor_channel_name[SENSOR_CHAN_ALL] = {
42 [SENSOR_CHAN_ACCEL_X] = "accel_x",
43 [SENSOR_CHAN_ACCEL_Y] = "accel_y",
44 [SENSOR_CHAN_ACCEL_Z] = "accel_z",
45 [SENSOR_CHAN_ACCEL_XYZ] = "accel_xyz",
46 [SENSOR_CHAN_GYRO_X] = "gyro_x",
47 [SENSOR_CHAN_GYRO_Y] = "gyro_y",
48 [SENSOR_CHAN_GYRO_Z] = "gyro_z",
49 [SENSOR_CHAN_GYRO_XYZ] = "gyro_xyz",
50 [SENSOR_CHAN_MAGN_X] = "magn_x",
51 [SENSOR_CHAN_MAGN_Y] = "magn_y",
52 [SENSOR_CHAN_MAGN_Z] = "magn_z",
53 [SENSOR_CHAN_MAGN_XYZ] = "magn_xyz",
54 [SENSOR_CHAN_DIE_TEMP] = "die_temp",
55 [SENSOR_CHAN_AMBIENT_TEMP] = "ambient_temp",
56 [SENSOR_CHAN_PRESS] = "press",
57 [SENSOR_CHAN_PROX] = "prox",
58 [SENSOR_CHAN_HUMIDITY] = "humidity",
59 [SENSOR_CHAN_LIGHT] = "light",
60 [SENSOR_CHAN_IR] = "ir",
61 [SENSOR_CHAN_RED] = "red",
62 [SENSOR_CHAN_GREEN] = "green",
63 [SENSOR_CHAN_BLUE] = "blue",
64 [SENSOR_CHAN_ALTITUDE] = "altitude",
65 [SENSOR_CHAN_PM_1_0] = "pm_1_0",
66 [SENSOR_CHAN_PM_2_5] = "pm_2_5",
67 [SENSOR_CHAN_PM_10] = "pm_10",
68 [SENSOR_CHAN_DISTANCE] = "distance",
69 [SENSOR_CHAN_CO2] = "co2",
70 [SENSOR_CHAN_VOC] = "voc",
71 [SENSOR_CHAN_GAS_RES] = "gas_resistance",
72 [SENSOR_CHAN_VOLTAGE] = "voltage",
73 [SENSOR_CHAN_CURRENT] = "current",
74 [SENSOR_CHAN_POWER] = "power",
75 [SENSOR_CHAN_RESISTANCE] = "resistance",
76 [SENSOR_CHAN_ROTATION] = "rotation",
77 [SENSOR_CHAN_POS_DX] = "pos_dx",
78 [SENSOR_CHAN_POS_DY] = "pos_dy",
79 [SENSOR_CHAN_POS_DZ] = "pos_dz",
80 [SENSOR_CHAN_RPM] = "rpm",
81 [SENSOR_CHAN_GAUGE_VOLTAGE] = "gauge_voltage",
82 [SENSOR_CHAN_GAUGE_AVG_CURRENT] = "gauge_avg_current",
83 [SENSOR_CHAN_GAUGE_STDBY_CURRENT] = "gauge_stdby_current",
84 [SENSOR_CHAN_GAUGE_MAX_LOAD_CURRENT] = "gauge_max_load_current",
85 [SENSOR_CHAN_GAUGE_TEMP] = "gauge_temp",
86 [SENSOR_CHAN_GAUGE_STATE_OF_CHARGE] = "gauge_state_of_charge",
87 [SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY] = "gauge_full_cap",
88 [SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY] = "gauge_remaining_cap",
89 [SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY] = "gauge_nominal_cap",
90 [SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY] = "gauge_full_avail_cap",
91 [SENSOR_CHAN_GAUGE_AVG_POWER] = "gauge_avg_power",
92 [SENSOR_CHAN_GAUGE_STATE_OF_HEALTH] = "gauge_state_of_health",
93 [SENSOR_CHAN_GAUGE_TIME_TO_EMPTY] = "gauge_time_to_empty",
94 [SENSOR_CHAN_GAUGE_TIME_TO_FULL] = "gauge_time_to_full",
95 [SENSOR_CHAN_GAUGE_CYCLE_COUNT] = "gauge_cycle_count",
96 [SENSOR_CHAN_GAUGE_DESIGN_VOLTAGE] = "gauge_design_voltage",
97 [SENSOR_CHAN_GAUGE_DESIRED_VOLTAGE] = "gauge_desired_voltage",
98 [SENSOR_CHAN_GAUGE_DESIRED_CHARGING_CURRENT] = "gauge_desired_charging_current",
99 };
100
101 static const char *sensor_attribute_name[SENSOR_ATTR_COMMON_COUNT] = {
102 [SENSOR_ATTR_SAMPLING_FREQUENCY] = "sampling_frequency",
103 [SENSOR_ATTR_LOWER_THRESH] = "lower_thresh",
104 [SENSOR_ATTR_UPPER_THRESH] = "upper_thresh",
105 [SENSOR_ATTR_SLOPE_TH] = "slope_th",
106 [SENSOR_ATTR_SLOPE_DUR] = "slope_dur",
107 [SENSOR_ATTR_HYSTERESIS] = "hysteresis",
108 [SENSOR_ATTR_OVERSAMPLING] = "oversampling",
109 [SENSOR_ATTR_FULL_SCALE] = "full_scale",
110 [SENSOR_ATTR_OFFSET] = "offset",
111 [SENSOR_ATTR_CALIB_TARGET] = "calib_target",
112 [SENSOR_ATTR_CONFIGURATION] = "configuration",
113 [SENSOR_ATTR_CALIBRATION] = "calibration",
114 [SENSOR_ATTR_FEATURE_MASK] = "feature_mask",
115 [SENSOR_ATTR_ALERT] = "alert",
116 [SENSOR_ATTR_FF_DUR] = "ff_dur",
117 };
118
119 /* Forward declaration */
120 static void data_ready_trigger_handler(const struct device *sensor,
121 const struct sensor_trigger *trigger);
122
123 #define TRIGGER_DATA_ENTRY(trig_enum, str_name, handler_func) \
124 [(trig_enum)] = {.name = #str_name, \
125 .handler = (handler_func), \
126 .trigger = {.chan = SENSOR_CHAN_ALL, .type = (trig_enum)}}
127
128 /**
129 * @brief This table stores a mapping of string trigger names along with the sensor_trigger struct
130 * that gets passed to the driver to enable that trigger, plus a function pointer to a handler. If
131 * that pointer is NULL, this indicates there is not currently support for that trigger type in the
132 * sensor shell.
133 */
134 static const struct {
135 const char *name;
136 sensor_trigger_handler_t handler;
137 struct sensor_trigger trigger;
138 } sensor_trigger_table[SENSOR_TRIG_COMMON_COUNT] = {
139 TRIGGER_DATA_ENTRY(SENSOR_TRIG_TIMER, timer, NULL),
140 TRIGGER_DATA_ENTRY(SENSOR_TRIG_DATA_READY, data_ready, data_ready_trigger_handler),
141 TRIGGER_DATA_ENTRY(SENSOR_TRIG_DELTA, delta, NULL),
142 TRIGGER_DATA_ENTRY(SENSOR_TRIG_NEAR_FAR, near_far, NULL),
143 TRIGGER_DATA_ENTRY(SENSOR_TRIG_THRESHOLD, threshold, NULL),
144 TRIGGER_DATA_ENTRY(SENSOR_TRIG_TAP, tap, NULL),
145 TRIGGER_DATA_ENTRY(SENSOR_TRIG_DOUBLE_TAP, double_tap, NULL),
146 TRIGGER_DATA_ENTRY(SENSOR_TRIG_FREEFALL, freefall, NULL),
147 TRIGGER_DATA_ENTRY(SENSOR_TRIG_MOTION, motion, NULL),
148 TRIGGER_DATA_ENTRY(SENSOR_TRIG_STATIONARY, stationary, NULL),
149 };
150
151 enum dynamic_command_context {
152 NONE,
153 CTX_GET,
154 CTX_ATTR_GET_SET,
155 };
156
157 static enum dynamic_command_context current_cmd_ctx = NONE;
158
159 /* Mutex for accessing shared RTIO/IODEV data structures */
160 K_MUTEX_DEFINE(cmd_get_mutex);
161
162 /* Crate a single common config for one-shot reading */
163 static enum sensor_channel iodev_sensor_shell_channels[SENSOR_CHAN_ALL];
164 static struct sensor_read_config iodev_sensor_shell_read_config = {
165 .sensor = NULL,
166 .channels = iodev_sensor_shell_channels,
167 .count = 0,
168 .max = ARRAY_SIZE(iodev_sensor_shell_channels),
169 };
170 RTIO_IODEV_DEFINE(iodev_sensor_shell_read, &__sensor_iodev_api, &iodev_sensor_shell_read_config);
171
172 /* Create the RTIO context to service the reading */
173 RTIO_DEFINE_WITH_MEMPOOL(sensor_read_rtio, 8, 8, 32, 64, 4);
174
parse_named_int(const char * name,const char * heystack[],size_t count)175 static int parse_named_int(const char *name, const char *heystack[], size_t count)
176 {
177 char *endptr;
178 int i;
179
180 /* Attempt to parse channel name as a number first */
181 i = strtoul(name, &endptr, 0);
182
183 if (*endptr == '\0') {
184 return i;
185 }
186
187 /* Channel name is not a number, look it up */
188 for (i = 0; i < count; i++) {
189 if (strcmp(name, heystack[i]) == 0) {
190 return i;
191 }
192 }
193
194 return -ENOTSUP;
195 }
196
parse_sensor_value(const char * val_str,struct sensor_value * out)197 static int parse_sensor_value(const char *val_str, struct sensor_value *out)
198 {
199 const bool is_negative = val_str[0] == '-';
200 const char *decimal_pos = strchr(val_str, '.');
201 long value;
202 char *endptr;
203
204 /* Parse int portion */
205 value = strtol(val_str, &endptr, 0);
206
207 if (*endptr != '\0' && *endptr != '.') {
208 return -EINVAL;
209 }
210 if (value > INT32_MAX || value < INT32_MIN) {
211 return -EINVAL;
212 }
213 out->val1 = (int32_t)value;
214
215 if (decimal_pos == NULL) {
216 return 0;
217 }
218
219 /* Parse the decimal portion */
220 value = strtoul(decimal_pos + 1, &endptr, 0);
221 if (*endptr != '\0') {
222 return -EINVAL;
223 }
224 while (value < 100000) {
225 value *= 10;
226 }
227 if (value > INT32_C(999999)) {
228 return -EINVAL;
229 }
230 out->val2 = (int32_t)value;
231 if (is_negative) {
232 out->val2 *= -1;
233 }
234 return 0;
235 }
236
237 struct sensor_shell_processing_context {
238 const struct device *dev;
239 const struct shell *sh;
240 };
241
sensor_shell_processing_callback(int result,uint8_t * buf,uint32_t buf_len,void * userdata)242 static void sensor_shell_processing_callback(int result, uint8_t *buf, uint32_t buf_len,
243 void *userdata)
244 {
245 struct sensor_shell_processing_context *ctx = userdata;
246 const struct sensor_decoder_api *decoder;
247 uint8_t decoded_buffer[128];
248 int rc;
249
250 ARG_UNUSED(buf_len);
251
252 if (result < 0) {
253 shell_error(ctx->sh, "Read failed");
254 return;
255 }
256
257 rc = sensor_get_decoder(ctx->dev, &decoder);
258 if (rc != 0) {
259 shell_error(ctx->sh, "Failed to get decoder for '%s'", ctx->dev->name);
260 return;
261 }
262
263 for (int channel = 0; channel < SENSOR_CHAN_ALL; ++channel) {
264 uint32_t fit = 0;
265 size_t base_size;
266 size_t frame_size;
267 size_t channel_idx = 0;
268 uint16_t frame_count;
269
270 if (channel == SENSOR_CHAN_ACCEL_X || channel == SENSOR_CHAN_ACCEL_Y ||
271 channel == SENSOR_CHAN_ACCEL_Z || channel == SENSOR_CHAN_GYRO_X ||
272 channel == SENSOR_CHAN_GYRO_Y || channel == SENSOR_CHAN_GYRO_Z ||
273 channel == SENSOR_CHAN_MAGN_X || channel == SENSOR_CHAN_MAGN_Y ||
274 channel == SENSOR_CHAN_MAGN_Z || channel == SENSOR_CHAN_POS_DY ||
275 channel == SENSOR_CHAN_POS_DZ) {
276 continue;
277 }
278
279 rc = decoder->get_size_info(channel, &base_size, &frame_size);
280 if (rc != 0) {
281 /* Channel not supported, skipping */
282 continue;
283 }
284
285 if (base_size > ARRAY_SIZE(decoded_buffer)) {
286 shell_error(ctx->sh,
287 "Channel (%d) requires %zu bytes to decode, but only %zu are "
288 "available",
289 channel, base_size, ARRAY_SIZE(decoded_buffer));
290 continue;
291 }
292
293 while (decoder->get_frame_count(buf, channel, channel_idx, &frame_count) == 0) {
294 fit = 0;
295 while (decoder->decode(buf, channel, channel_idx, &fit, 1, decoded_buffer) >
296 0) {
297
298 switch (channel) {
299 case SENSOR_CHAN_ACCEL_XYZ:
300 case SENSOR_CHAN_GYRO_XYZ:
301 case SENSOR_CHAN_MAGN_XYZ:
302 case SENSOR_CHAN_POS_DX: {
303 struct sensor_three_axis_data *data =
304 (struct sensor_three_axis_data *)decoded_buffer;
305
306 shell_info(ctx->sh,
307 "channel idx=%d %s shift=%d "
308 "value=%" PRIsensor_three_axis_data,
309 channel, sensor_channel_name[channel],
310 data->shift,
311 PRIsensor_three_axis_data_arg(*data, 0));
312 break;
313 }
314 case SENSOR_CHAN_PROX: {
315 struct sensor_byte_data *data =
316 (struct sensor_byte_data *)decoded_buffer;
317
318 shell_info(ctx->sh,
319 "channel idx=%d %s value=%" PRIsensor_byte_data(
320 is_near),
321 channel, sensor_channel_name[channel],
322 PRIsensor_byte_data_arg(*data, 0, is_near));
323 break;
324 }
325 default: {
326 struct sensor_q31_data *data =
327 (struct sensor_q31_data *)decoded_buffer;
328
329 shell_info(ctx->sh,
330 "channel idx=%d %s shift=%d "
331 "value=%" PRIsensor_q31_data,
332 channel,
333 (channel >= ARRAY_SIZE(sensor_channel_name))
334 ? ""
335 : sensor_channel_name[channel],
336 data->shift, PRIsensor_q31_data_arg(*data, 0));
337 break;
338 }
339 }
340 }
341 ++channel_idx;
342 }
343 }
344 }
345
cmd_get_sensor(const struct shell * sh,size_t argc,char * argv[])346 static int cmd_get_sensor(const struct shell *sh, size_t argc, char *argv[])
347 {
348 const struct device *dev;
349 int count = 0;
350 int err;
351
352 err = k_mutex_lock(&cmd_get_mutex, K_NO_WAIT);
353 if (err < 0) {
354 shell_error(sh, "Another sensor reading in progress");
355 return err;
356 }
357
358 dev = device_get_binding(argv[1]);
359 if (dev == NULL) {
360 shell_error(sh, "Device unknown (%s)", argv[1]);
361 k_mutex_unlock(&cmd_get_mutex);
362 return -ENODEV;
363 }
364
365 if (argc == 2) {
366 /* read all channels */
367 for (int i = 0; i < ARRAY_SIZE(iodev_sensor_shell_channels); ++i) {
368 if (SENSOR_CHANNEL_3_AXIS(i)) {
369 continue;
370 }
371 iodev_sensor_shell_channels[count++] = i;
372 }
373 } else {
374 /* read specific channels */
375 for (int i = 2; i < argc; ++i) {
376 int chan = parse_named_int(argv[i], sensor_channel_name,
377 ARRAY_SIZE(sensor_channel_name));
378
379 if (chan < 0) {
380 shell_error(sh, "Failed to read channel (%s)", argv[i]);
381 continue;
382 }
383 iodev_sensor_shell_channels[count++] = chan;
384 }
385 }
386
387 if (count == 0) {
388 shell_error(sh, "No channels to read, bailing");
389 k_mutex_unlock(&cmd_get_mutex);
390 return -EINVAL;
391 }
392 iodev_sensor_shell_read_config.sensor = dev;
393 iodev_sensor_shell_read_config.count = count;
394
395 struct sensor_shell_processing_context ctx = {
396 .dev = dev,
397 .sh = sh,
398 };
399 err = sensor_read(&iodev_sensor_shell_read, &sensor_read_rtio, &ctx);
400 if (err < 0) {
401 shell_error(sh, "Failed to read sensor: %d", err);
402 }
403 sensor_processing_with_callback(&sensor_read_rtio, sensor_shell_processing_callback);
404
405 k_mutex_unlock(&cmd_get_mutex);
406
407 return 0;
408 }
409
cmd_sensor_attr_set(const struct shell * shell_ptr,size_t argc,char * argv[])410 static int cmd_sensor_attr_set(const struct shell *shell_ptr, size_t argc, char *argv[])
411 {
412 const struct device *dev;
413 int rc;
414
415 dev = device_get_binding(argv[1]);
416 if (dev == NULL) {
417 shell_error(shell_ptr, "Device unknown (%s)", argv[1]);
418 return -ENODEV;
419 }
420
421 for (size_t i = 2; i < argc; i += 3) {
422 int channel = parse_named_int(argv[i], sensor_channel_name,
423 ARRAY_SIZE(sensor_channel_name));
424 int attr = parse_named_int(argv[i + 1], sensor_attribute_name,
425 ARRAY_SIZE(sensor_attribute_name));
426 struct sensor_value value = {0};
427
428 if (channel < 0) {
429 shell_error(shell_ptr, "Channel '%s' unknown", argv[i]);
430 return -EINVAL;
431 }
432 if (attr < 0) {
433 shell_error(shell_ptr, "Attribute '%s' unknown", argv[i + 1]);
434 return -EINVAL;
435 }
436 if (parse_sensor_value(argv[i + 2], &value)) {
437 shell_error(shell_ptr, "Sensor value '%s' invalid", argv[i + 2]);
438 return -EINVAL;
439 }
440
441 rc = sensor_attr_set(dev, channel, attr, &value);
442 if (rc) {
443 shell_error(shell_ptr, "Failed to set channel(%s) attribute(%s): %d",
444 sensor_channel_name[channel], sensor_attribute_name[attr], rc);
445 continue;
446 }
447 shell_info(shell_ptr, "%s channel=%s, attr=%s set to value=%s", dev->name,
448 sensor_channel_name[channel], sensor_attribute_name[attr], argv[i + 2]);
449 }
450 return 0;
451 }
452
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)453 static void cmd_sensor_attr_get_handler(const struct shell *shell_ptr, const struct device *dev,
454 const char *channel_name, const char *attr_name,
455 bool print_missing_attribute)
456 {
457 int channel =
458 parse_named_int(channel_name, sensor_channel_name, ARRAY_SIZE(sensor_channel_name));
459 int attr = parse_named_int(attr_name, sensor_attribute_name,
460 ARRAY_SIZE(sensor_attribute_name));
461 struct sensor_value value = {0};
462 int rc;
463
464 if (channel < 0) {
465 shell_error(shell_ptr, "Channel '%s' unknown", channel_name);
466 return;
467 }
468 if (attr < 0) {
469 shell_error(shell_ptr, "Attribute '%s' unknown", attr_name);
470 return;
471 }
472
473 rc = sensor_attr_get(dev, channel, attr, &value);
474
475 if (rc != 0) {
476 if (rc == -EINVAL && !print_missing_attribute) {
477 return;
478 }
479 shell_error(shell_ptr, "Failed to get channel(%s) attribute(%s): %d",
480 sensor_channel_name[channel], sensor_attribute_name[attr], rc);
481 return;
482 }
483
484 shell_info(shell_ptr, "%s(channel=%s, attr=%s) value=%.6f", dev->name,
485 sensor_channel_name[channel], sensor_attribute_name[attr],
486 sensor_value_to_double(&value));
487 }
488
cmd_sensor_attr_get(const struct shell * shell_ptr,size_t argc,char * argv[])489 static int cmd_sensor_attr_get(const struct shell *shell_ptr, size_t argc, char *argv[])
490 {
491 const struct device *dev;
492
493 dev = device_get_binding(argv[1]);
494 if (dev == NULL) {
495 shell_error(shell_ptr, "Device unknown (%s)", argv[1]);
496 return -ENODEV;
497 }
498
499 if (argc > 2) {
500 for (size_t i = 2; i < argc; i += 2) {
501 cmd_sensor_attr_get_handler(shell_ptr, dev, argv[i], argv[i + 1],
502 /*print_missing_attribute=*/true);
503 }
504 } else {
505 for (size_t channel_idx = 0; channel_idx < ARRAY_SIZE(sensor_channel_name);
506 ++channel_idx) {
507 for (size_t attr_idx = 0; attr_idx < ARRAY_SIZE(sensor_attribute_name);
508 ++attr_idx) {
509 cmd_sensor_attr_get_handler(shell_ptr, dev,
510 sensor_channel_name[channel_idx],
511 sensor_attribute_name[attr_idx],
512 /*print_missing_attribute=*/false);
513 }
514 }
515 }
516 return 0;
517 }
518
519 static void channel_name_get(size_t idx, struct shell_static_entry *entry);
520
521 SHELL_DYNAMIC_CMD_CREATE(dsub_channel_name, channel_name_get);
522
attribute_name_get(size_t idx,struct shell_static_entry * entry)523 static void attribute_name_get(size_t idx, struct shell_static_entry *entry)
524 {
525 int cnt = 0;
526
527 entry->syntax = NULL;
528 entry->handler = NULL;
529 entry->help = NULL;
530 entry->subcmd = &dsub_channel_name;
531
532 for (int i = 0; i < SENSOR_ATTR_COMMON_COUNT; i++) {
533 if (sensor_attribute_name[i] != NULL) {
534 if (cnt == idx) {
535 entry->syntax = sensor_attribute_name[i];
536 break;
537 }
538 cnt++;
539 }
540 }
541 }
542 SHELL_DYNAMIC_CMD_CREATE(dsub_attribute_name, attribute_name_get);
543
channel_name_get(size_t idx,struct shell_static_entry * entry)544 static void channel_name_get(size_t idx, struct shell_static_entry *entry)
545 {
546 int cnt = 0;
547
548 entry->syntax = NULL;
549 entry->handler = NULL;
550 entry->help = NULL;
551 if (current_cmd_ctx == CTX_GET) {
552 entry->subcmd = &dsub_channel_name;
553 } else if (current_cmd_ctx == CTX_ATTR_GET_SET) {
554 entry->subcmd = &dsub_attribute_name;
555 } else {
556 entry->subcmd = NULL;
557 }
558
559 for (int i = 0; i < SENSOR_CHAN_ALL; i++) {
560 if (sensor_channel_name[i] != NULL) {
561 if (cnt == idx) {
562 entry->syntax = sensor_channel_name[i];
563 break;
564 }
565 cnt++;
566 }
567 }
568 }
569
570 static void device_name_get(size_t idx, struct shell_static_entry *entry);
571
572 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
573
device_name_get(size_t idx,struct shell_static_entry * entry)574 static void device_name_get(size_t idx, struct shell_static_entry *entry)
575 {
576 const struct device *dev = shell_device_lookup(idx, NULL);
577
578 current_cmd_ctx = CTX_GET;
579 entry->syntax = (dev != NULL) ? dev->name : NULL;
580 entry->handler = NULL;
581 entry->help = NULL;
582 entry->subcmd = &dsub_channel_name;
583 }
584
device_name_get_for_attr(size_t idx,struct shell_static_entry * entry)585 static void device_name_get_for_attr(size_t idx, struct shell_static_entry *entry)
586 {
587 const struct device *dev = shell_device_lookup(idx, NULL);
588
589 current_cmd_ctx = CTX_ATTR_GET_SET;
590 entry->syntax = (dev != NULL) ? dev->name : NULL;
591 entry->handler = NULL;
592 entry->help = NULL;
593 entry->subcmd = &dsub_channel_name;
594 }
595 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name_for_attr, device_name_get_for_attr);
596
trigger_name_get(size_t idx,struct shell_static_entry * entry)597 static void trigger_name_get(size_t idx, struct shell_static_entry *entry)
598 {
599 int cnt = 0;
600
601 entry->syntax = NULL;
602 entry->handler = NULL;
603 entry->help = NULL;
604 entry->subcmd = NULL;
605
606 for (int i = 0; i < SENSOR_TRIG_COMMON_COUNT; i++) {
607 if (sensor_trigger_table[i].name != NULL) {
608 if (cnt == idx) {
609 entry->syntax = sensor_trigger_table[i].name;
610 break;
611 }
612 cnt++;
613 }
614 }
615 }
616
617 SHELL_DYNAMIC_CMD_CREATE(dsub_trigger_name, trigger_name_get);
618
trigger_on_off_get(size_t idx,struct shell_static_entry * entry)619 static void trigger_on_off_get(size_t idx, struct shell_static_entry *entry)
620 {
621 entry->handler = NULL;
622 entry->help = NULL;
623 entry->subcmd = &dsub_trigger_name;
624
625 switch (idx) {
626 case 0:
627 entry->syntax = "on";
628 break;
629 case 1:
630 entry->syntax = "off";
631 break;
632 default:
633 entry->syntax = NULL;
634 break;
635 }
636 }
637
638 SHELL_DYNAMIC_CMD_CREATE(dsub_trigger_onoff, trigger_on_off_get);
639
device_name_get_for_trigger(size_t idx,struct shell_static_entry * entry)640 static void device_name_get_for_trigger(size_t idx, struct shell_static_entry *entry)
641 {
642 const struct device *dev = shell_device_lookup(idx, NULL);
643
644 entry->syntax = (dev != NULL) ? dev->name : NULL;
645 entry->handler = NULL;
646 entry->help = NULL;
647 entry->subcmd = &dsub_trigger_onoff;
648 }
649
650 SHELL_DYNAMIC_CMD_CREATE(dsub_trigger, device_name_get_for_trigger);
651
cmd_get_sensor_info(const struct shell * sh,size_t argc,char ** argv)652 static int cmd_get_sensor_info(const struct shell *sh, size_t argc, char **argv)
653 {
654 ARG_UNUSED(argc);
655 ARG_UNUSED(argv);
656
657 #ifdef CONFIG_SENSOR_INFO
658 const char *null_str = "(null)";
659
660 STRUCT_SECTION_FOREACH(sensor_info, sensor) {
661 shell_print(sh,
662 "device name: %s, vendor: %s, model: %s, "
663 "friendly name: %s",
664 sensor->dev->name, sensor->vendor ? sensor->vendor : null_str,
665 sensor->model ? sensor->model : null_str,
666 sensor->friendly_name ? sensor->friendly_name : null_str);
667 }
668 return 0;
669 #else
670 return -EINVAL;
671 #endif
672 }
673
674 enum sample_stats_state {
675 SAMPLE_STATS_STATE_UNINITIALIZED = 0,
676 SAMPLE_STATS_STATE_ENABLED,
677 SAMPLE_STATS_STATE_DISABLED,
678 };
679
680 struct sample_stats {
681 int64_t accumulator;
682 uint32_t count;
683 uint64_t sample_window_start;
684 enum sample_stats_state state;
685 };
686
data_ready_trigger_handler(const struct device * sensor,const struct sensor_trigger * trigger)687 static void data_ready_trigger_handler(const struct device *sensor,
688 const struct sensor_trigger *trigger)
689 {
690 static struct sample_stats stats[SENSOR_CHAN_ALL];
691 const int64_t now = k_uptime_get();
692 struct sensor_value value;
693
694 if (sensor_sample_fetch(sensor)) {
695 LOG_ERR("Failed to fetch samples on data ready handler");
696 }
697 for (int i = 0; i < SENSOR_CHAN_ALL; ++i) {
698 int rc;
699
700 /* Skip disabled channels */
701 if (stats[i].state == SAMPLE_STATS_STATE_DISABLED) {
702 continue;
703 }
704 /* Skip 3 axis channels */
705 if (i == SENSOR_CHAN_ACCEL_XYZ || i == SENSOR_CHAN_GYRO_XYZ ||
706 i == SENSOR_CHAN_MAGN_XYZ) {
707 continue;
708 }
709
710 rc = sensor_channel_get(sensor, i, &value);
711 if (rc == -ENOTSUP && stats[i].state == SAMPLE_STATS_STATE_UNINITIALIZED) {
712 /* Stop reading this channel if the driver told us it's not supported. */
713 stats[i].state = SAMPLE_STATS_STATE_DISABLED;
714 }
715 if (rc != 0) {
716 /* Skip on any error. */
717 continue;
718 }
719 /* Do something with the data */
720 stats[i].accumulator += value.val1 * INT64_C(1000000) + value.val2;
721 if (stats[i].count++ == 0) {
722 stats[i].sample_window_start = now;
723 } else if (now > stats[i].sample_window_start +
724 CONFIG_SENSOR_SHELL_TRIG_PRINT_TIMEOUT_MS) {
725 int64_t micro_value = stats[i].accumulator / stats[i].count;
726
727 value.val1 = micro_value / 1000000;
728 value.val2 = (int32_t)llabs(micro_value - (value.val1 * 1000000));
729 LOG_INF("chan=%d, num_samples=%u, data=%d.%06d", i, stats[i].count,
730 value.val1, value.val2);
731
732 stats[i].accumulator = 0;
733 stats[i].count = 0;
734 }
735 }
736 }
737
cmd_trig_sensor(const struct shell * sh,size_t argc,char ** argv)738 static int cmd_trig_sensor(const struct shell *sh, size_t argc, char **argv)
739 {
740 const struct device *dev;
741 enum sensor_trigger_type trigger;
742 int err;
743
744 if (argc < 4) {
745 shell_error(sh, "Wrong number of args");
746 return -EINVAL;
747 }
748
749 /* Parse device name */
750 dev = device_get_binding(argv[1]);
751 if (dev == NULL) {
752 shell_error(sh, "Device unknown (%s)", argv[1]);
753 return -ENODEV;
754 }
755
756 /* Map the trigger string to an enum value */
757 for (trigger = 0; trigger < ARRAY_SIZE(sensor_trigger_table); trigger++) {
758 if (strcmp(argv[3], sensor_trigger_table[trigger].name) == 0) {
759 break;
760 }
761 }
762 if (trigger >= SENSOR_TRIG_COMMON_COUNT || sensor_trigger_table[trigger].handler == NULL) {
763 shell_error(sh, "Unsupported trigger type (%s)", argv[3]);
764 return -ENOTSUP;
765 }
766
767 /* Parse on/off */
768 if (strcmp(argv[2], "on") == 0) {
769 err = sensor_trigger_set(dev, &sensor_trigger_table[trigger].trigger,
770 sensor_trigger_table[trigger].handler);
771 } else if (strcmp(argv[2], "off") == 0) {
772 /* Clear the handler for the given trigger on this device */
773 err = sensor_trigger_set(dev, &sensor_trigger_table[trigger].trigger, NULL);
774 } else {
775 shell_error(sh, "Pass 'on' or 'off' to enable/disable trigger");
776 return -EINVAL;
777 }
778
779 if (err) {
780 shell_error(sh, "Error while setting trigger %d on device %s (%d)", trigger,
781 argv[1], err);
782 }
783
784 return err;
785 }
786
787 /* clang-format off */
788 SHELL_STATIC_SUBCMD_SET_CREATE(sub_sensor,
789 SHELL_CMD_ARG(get, &dsub_device_name, SENSOR_GET_HELP, cmd_get_sensor,
790 2, 255),
791 SHELL_CMD_ARG(attr_set, &dsub_device_name_for_attr, SENSOR_ATTR_SET_HELP,
792 cmd_sensor_attr_set, 2, 255),
793 SHELL_CMD_ARG(attr_get, &dsub_device_name_for_attr, SENSOR_ATTR_GET_HELP,
794 cmd_sensor_attr_get, 2, 255),
795 SHELL_COND_CMD(CONFIG_SENSOR_INFO, info, NULL, SENSOR_INFO_HELP,
796 cmd_get_sensor_info),
797 SHELL_CMD_ARG(trig, &dsub_trigger, SENSOR_TRIG_HELP, cmd_trig_sensor,
798 2, 255),
799 SHELL_SUBCMD_SET_END
800 );
801 /* clang-format on */
802
803 SHELL_CMD_REGISTER(sensor, &sub_sensor, "Sensor commands", NULL);
804