/* * Copyright (c) 2018 Diego Sueiro * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #define SENSOR_GET_HELP \ "Get sensor data. Channel names are optional. All channels are read " \ "when no channels are provided. Syntax:\n" \ " .. " #define SENSOR_ATTR_GET_HELP \ "Get the sensor's channel attribute. Syntax:\n" \ " [ .. " \ " ]" #define SENSOR_ATTR_SET_HELP \ "Set the sensor's channel attribute.\n" \ " " #define SENSOR_INFO_HELP "Get sensor info, such as vendor and model name, for all sensors." const char *sensor_channel_name[SENSOR_CHAN_ALL] = { [SENSOR_CHAN_ACCEL_X] = "accel_x", [SENSOR_CHAN_ACCEL_Y] = "accel_y", [SENSOR_CHAN_ACCEL_Z] = "accel_z", [SENSOR_CHAN_ACCEL_XYZ] = "accel_xyz", [SENSOR_CHAN_GYRO_X] = "gyro_x", [SENSOR_CHAN_GYRO_Y] = "gyro_y", [SENSOR_CHAN_GYRO_Z] = "gyro_z", [SENSOR_CHAN_GYRO_XYZ] = "gyro_xyz", [SENSOR_CHAN_MAGN_X] = "magn_x", [SENSOR_CHAN_MAGN_Y] = "magn_y", [SENSOR_CHAN_MAGN_Z] = "magn_z", [SENSOR_CHAN_MAGN_XYZ] = "magn_xyz", [SENSOR_CHAN_DIE_TEMP] = "die_temp", [SENSOR_CHAN_AMBIENT_TEMP] = "ambient_temp", [SENSOR_CHAN_PRESS] = "press", [SENSOR_CHAN_PROX] = "prox", [SENSOR_CHAN_HUMIDITY] = "humidity", [SENSOR_CHAN_LIGHT] = "light", [SENSOR_CHAN_IR] = "ir", [SENSOR_CHAN_RED] = "red", [SENSOR_CHAN_GREEN] = "green", [SENSOR_CHAN_BLUE] = "blue", [SENSOR_CHAN_ALTITUDE] = "altitude", [SENSOR_CHAN_PM_1_0] = "pm_1_0", [SENSOR_CHAN_PM_2_5] = "pm_2_5", [SENSOR_CHAN_PM_10] = "pm_10", [SENSOR_CHAN_DISTANCE] = "distance", [SENSOR_CHAN_CO2] = "co2", [SENSOR_CHAN_VOC] = "voc", [SENSOR_CHAN_GAS_RES] = "gas_resistance", [SENSOR_CHAN_VOLTAGE] = "voltage", [SENSOR_CHAN_CURRENT] = "current", [SENSOR_CHAN_POWER] = "power", [SENSOR_CHAN_RESISTANCE] = "resistance", [SENSOR_CHAN_ROTATION] = "rotation", [SENSOR_CHAN_POS_DX] = "pos_dx", [SENSOR_CHAN_POS_DY] = "pos_dy", [SENSOR_CHAN_POS_DZ] = "pos_dz", [SENSOR_CHAN_RPM] = "rpm", [SENSOR_CHAN_GAUGE_VOLTAGE] = "gauge_voltage", [SENSOR_CHAN_GAUGE_AVG_CURRENT] = "gauge_avg_current", [SENSOR_CHAN_GAUGE_STDBY_CURRENT] = "gauge_stdby_current", [SENSOR_CHAN_GAUGE_MAX_LOAD_CURRENT] = "gauge_max_load_current", [SENSOR_CHAN_GAUGE_TEMP] = "gauge_temp", [SENSOR_CHAN_GAUGE_STATE_OF_CHARGE] = "gauge_state_of_charge", [SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY] = "gauge_full_cap", [SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY] = "gauge_remaining_cap", [SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY] = "gauge_nominal_cap", [SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY] = "gauge_full_avail_cap", [SENSOR_CHAN_GAUGE_AVG_POWER] = "gauge_avg_power", [SENSOR_CHAN_GAUGE_STATE_OF_HEALTH] = "gauge_state_of_health", [SENSOR_CHAN_GAUGE_TIME_TO_EMPTY] = "gauge_time_to_empty", [SENSOR_CHAN_GAUGE_TIME_TO_FULL] = "gauge_time_to_full", [SENSOR_CHAN_GAUGE_CYCLE_COUNT] = "gauge_cycle_count", [SENSOR_CHAN_GAUGE_DESIGN_VOLTAGE] = "gauge_design_voltage", [SENSOR_CHAN_GAUGE_DESIRED_VOLTAGE] = "gauge_desired_voltage", [SENSOR_CHAN_GAUGE_DESIRED_CHARGING_CURRENT] = "gauge_desired_charging_current", }; static const char *sensor_attribute_name[SENSOR_ATTR_COMMON_COUNT] = { [SENSOR_ATTR_SAMPLING_FREQUENCY] = "sampling_frequency", [SENSOR_ATTR_LOWER_THRESH] = "lower_thresh", [SENSOR_ATTR_UPPER_THRESH] = "upper_thresh", [SENSOR_ATTR_SLOPE_TH] = "slope_th", [SENSOR_ATTR_SLOPE_DUR] = "slope_dur", [SENSOR_ATTR_HYSTERESIS] = "hysteresis", [SENSOR_ATTR_OVERSAMPLING] = "oversampling", [SENSOR_ATTR_FULL_SCALE] = "full_scale", [SENSOR_ATTR_OFFSET] = "offset", [SENSOR_ATTR_CALIB_TARGET] = "calib_target", [SENSOR_ATTR_CONFIGURATION] = "configuration", [SENSOR_ATTR_CALIBRATION] = "calibration", [SENSOR_ATTR_FEATURE_MASK] = "feature_mask", [SENSOR_ATTR_ALERT] = "alert", [SENSOR_ATTR_FF_DUR] = "ff_dur", }; enum dynamic_command_context { NONE, CTX_GET, CTX_ATTR_GET_SET, }; static enum dynamic_command_context current_cmd_ctx = NONE; static int parse_named_int(const char *name, const char *heystack[], size_t count) { char *endptr; int i; /* Attempt to parse channel name as a number first */ i = strtoul(name, &endptr, 0); if (*endptr == '\0') { return i; } /* Channel name is not a number, look it up */ for (i = 0; i < count; i++) { if (strcmp(name, heystack[i]) == 0) { return i; } } return -ENOTSUP; } static int parse_sensor_value(const char *val_str, struct sensor_value *out) { const bool is_negative = val_str[0] == '-'; const char *decimal_pos = strchr(val_str, '.'); long value; char *endptr; /* Parse int portion */ value = strtol(val_str, &endptr, 0); if (*endptr != '\0' && *endptr != '.') { return -EINVAL; } if (value > INT32_MAX || value < INT32_MIN) { return -EINVAL; } out->val1 = (int32_t)value; if (decimal_pos == NULL) { return 0; } /* Parse the decimal portion */ value = strtoul(decimal_pos + 1, &endptr, 0); if (*endptr != '\0') { return -EINVAL; } while (value < 100000) { value *= 10; } if (value > INT32_C(999999)) { return -EINVAL; } out->val2 = (int32_t)value; if (is_negative) { out->val2 *= -1; } return 0; } static int handle_channel_by_name(const struct shell *shell_ptr, const struct device *dev, const char *channel_name) { struct sensor_value value[3]; int err; const int i = parse_named_int(channel_name, sensor_channel_name, ARRAY_SIZE(sensor_channel_name)); if (i < 0) { shell_error(shell_ptr, "Channel not supported (%s)", channel_name); return i; } err = sensor_channel_get(dev, i, value); if (err < 0) { return err; } if (i >= ARRAY_SIZE(sensor_channel_name)) { shell_print(shell_ptr, "channel idx=%d value = %10.6f", i, sensor_value_to_double(&value[0])); } else if (i != SENSOR_CHAN_ACCEL_XYZ && i != SENSOR_CHAN_GYRO_XYZ && i != SENSOR_CHAN_MAGN_XYZ) { shell_print(shell_ptr, "channel idx=%d %s = %10.6f", i, sensor_channel_name[i], sensor_value_to_double(&value[0])); } else { /* clang-format off */ shell_print(shell_ptr, "channel idx=%d %s x = %10.6f y = %10.6f z = %10.6f", i, sensor_channel_name[i], sensor_value_to_double(&value[0]), sensor_value_to_double(&value[1]), sensor_value_to_double(&value[2])); /* clang-format on */ } return 0; } static int cmd_get_sensor(const struct shell *shell, size_t argc, char *argv[]) { const struct device *dev; int err; dev = device_get_binding(argv[1]); if (dev == NULL) { shell_error(shell, "Device unknown (%s)", argv[1]); return -ENODEV; } err = sensor_sample_fetch(dev); if (err < 0) { shell_error(shell, "Failed to read sensor: %d", err); } if (argc == 2) { /* read all channels */ for (int i = 0; i < ARRAY_SIZE(sensor_channel_name); i++) { if (sensor_channel_name[i]) { handle_channel_by_name(shell, dev, sensor_channel_name[i]); } } } else { for (int i = 2; i < argc; i++) { err = handle_channel_by_name(shell, dev, argv[i]); if (err < 0) { shell_error(shell, "Failed to read channel (%s)", argv[i]); } } } return 0; } static int cmd_sensor_attr_set(const struct shell *shell_ptr, size_t argc, char *argv[]) { const struct device *dev; int rc; dev = device_get_binding(argv[1]); if (dev == NULL) { shell_error(shell_ptr, "Device unknown (%s)", argv[1]); return -ENODEV; } for (size_t i = 2; i < argc; i += 3) { int channel = parse_named_int(argv[i], sensor_channel_name, ARRAY_SIZE(sensor_channel_name)); int attr = parse_named_int(argv[i + 1], sensor_attribute_name, ARRAY_SIZE(sensor_attribute_name)); struct sensor_value value = {0}; if (channel < 0) { shell_error(shell_ptr, "Channel '%s' unknown", argv[i]); return -EINVAL; } if (attr < 0) { shell_error(shell_ptr, "Attribute '%s' unknown", argv[i + 1]); return -EINVAL; } if (parse_sensor_value(argv[i + 2], &value)) { shell_error(shell_ptr, "Sensor value '%s' invalid", argv[i + 2]); return -EINVAL; } rc = sensor_attr_set(dev, channel, attr, &value); if (rc) { shell_error(shell_ptr, "Failed to set channel(%s) attribute(%s): %d", sensor_channel_name[channel], sensor_attribute_name[attr], rc); continue; } shell_info(shell_ptr, "%s channel=%s, attr=%s set to value=%s", dev->name, sensor_channel_name[channel], sensor_attribute_name[attr], argv[i + 2]); } return 0; } static void 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) { int channel = parse_named_int(channel_name, sensor_channel_name, ARRAY_SIZE(sensor_channel_name)); int attr = parse_named_int(attr_name, sensor_attribute_name, ARRAY_SIZE(sensor_attribute_name)); struct sensor_value value = {0}; int rc; if (channel < 0) { shell_error(shell_ptr, "Channel '%s' unknown", channel_name); return; } if (attr < 0) { shell_error(shell_ptr, "Attribute '%s' unknown", attr_name); return; } rc = sensor_attr_get(dev, channel, attr, &value); if (rc != 0) { if (rc == -EINVAL && !print_missing_attribute) { return; } shell_error(shell_ptr, "Failed to get channel(%s) attribute(%s): %d", sensor_channel_name[channel], sensor_attribute_name[attr], rc); return; } shell_info(shell_ptr, "%s(channel=%s, attr=%s) value=%.6f", dev->name, sensor_channel_name[channel], sensor_attribute_name[attr], sensor_value_to_double(&value)); } static int cmd_sensor_attr_get(const struct shell *shell_ptr, size_t argc, char *argv[]) { const struct device *dev; dev = device_get_binding(argv[1]); if (dev == NULL) { shell_error(shell_ptr, "Device unknown (%s)", argv[1]); return -ENODEV; } if (argc > 2) { for (size_t i = 2; i < argc; i += 2) { cmd_sensor_attr_get_handler(shell_ptr, dev, argv[i], argv[i + 1], /*print_missing_attribute=*/true); } } else { for (size_t channel_idx = 0; channel_idx < ARRAY_SIZE(sensor_channel_name); ++channel_idx) { for (size_t attr_idx = 0; attr_idx < ARRAY_SIZE(sensor_attribute_name); ++attr_idx) { cmd_sensor_attr_get_handler(shell_ptr, dev, sensor_channel_name[channel_idx], sensor_attribute_name[attr_idx], /*print_missing_attribute=*/false); } } } return 0; } static void channel_name_get(size_t idx, struct shell_static_entry *entry); SHELL_DYNAMIC_CMD_CREATE(dsub_channel_name, channel_name_get); static void attribute_name_get(size_t idx, struct shell_static_entry *entry) { int cnt = 0; entry->syntax = NULL; entry->handler = NULL; entry->help = NULL; entry->subcmd = &dsub_channel_name; for (int i = 0; i < SENSOR_ATTR_COMMON_COUNT; i++) { if (sensor_attribute_name[i] != NULL) { if (cnt == idx) { entry->syntax = sensor_attribute_name[i]; break; } cnt++; } } } SHELL_DYNAMIC_CMD_CREATE(dsub_attribute_name, attribute_name_get); static void channel_name_get(size_t idx, struct shell_static_entry *entry) { int cnt = 0; entry->syntax = NULL; entry->handler = NULL; entry->help = NULL; if (current_cmd_ctx == CTX_GET) { entry->subcmd = &dsub_channel_name; } else if (current_cmd_ctx == CTX_ATTR_GET_SET) { entry->subcmd = &dsub_attribute_name; } else { entry->subcmd = NULL; } for (int i = 0; i < SENSOR_CHAN_ALL; i++) { if (sensor_channel_name[i] != NULL) { if (cnt == idx) { entry->syntax = sensor_channel_name[i]; break; } cnt++; } } } static void device_name_get(size_t idx, struct shell_static_entry *entry); SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get); static void device_name_get(size_t idx, struct shell_static_entry *entry) { const struct device *dev = shell_device_lookup(idx, NULL); current_cmd_ctx = CTX_GET; entry->syntax = (dev != NULL) ? dev->name : NULL; entry->handler = NULL; entry->help = NULL; entry->subcmd = &dsub_channel_name; } static void device_name_get_for_attr(size_t idx, struct shell_static_entry *entry) { const struct device *dev = shell_device_lookup(idx, NULL); current_cmd_ctx = CTX_ATTR_GET_SET; entry->syntax = (dev != NULL) ? dev->name : NULL; entry->handler = NULL; entry->help = NULL; entry->subcmd = &dsub_channel_name; } SHELL_DYNAMIC_CMD_CREATE(dsub_device_name_for_attr, device_name_get_for_attr); static int cmd_get_sensor_info(const struct shell *sh, size_t argc, char **argv) { ARG_UNUSED(argc); ARG_UNUSED(argv); #ifdef CONFIG_SENSOR_INFO const char *null_str = "(null)"; STRUCT_SECTION_FOREACH(sensor_info, sensor) { shell_print(sh, "device name: %s, vendor: %s, model: %s, " "friendly name: %s", sensor->dev->name, sensor->vendor ? sensor->vendor : null_str, sensor->model ? sensor->model : null_str, sensor->friendly_name ? sensor->friendly_name : null_str); } return 0; #else return -EINVAL; #endif } /* clang-format off */ SHELL_STATIC_SUBCMD_SET_CREATE(sub_sensor, SHELL_CMD_ARG(get, &dsub_device_name, SENSOR_GET_HELP, cmd_get_sensor, 2, 255), SHELL_CMD_ARG(attr_set, &dsub_device_name_for_attr, SENSOR_ATTR_SET_HELP, cmd_sensor_attr_set, 2, 255), SHELL_CMD_ARG(attr_get, &dsub_device_name_for_attr, SENSOR_ATTR_GET_HELP, cmd_sensor_attr_get, 2, 255), SHELL_COND_CMD(CONFIG_SENSOR_INFO, info, NULL, SENSOR_INFO_HELP, cmd_get_sensor_info), SHELL_SUBCMD_SET_END ); /* clang-format on */ SHELL_CMD_REGISTER(sensor, &sub_sensor, "Sensor commands", NULL);