blob: 37428fc5ee1806f3187b762ceefa7a8ed875e9a3 [file] [log] [blame]
/*
* Copyright (c) 2018 Diego Sueiro
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/shell/shell.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#define SENSOR_GET_HELP \
"Get sensor data. Channel names are optional. All channels are read " \
"when no channels are provided. Syntax:\n" \
"<device_name> <channel name 0> .. <channel name N>"
#define SENSOR_ATTR_GET_HELP \
"Get the sensor's channel attribute. Syntax:\n" \
"<device_name> [<channel_name 0> <attribute_name 0> .. " \
"<channel_name N> <attribute_name N>]"
#define SENSOR_ATTR_SET_HELP \
"Set the sensor's channel attribute.\n" \
"<device_name> <channel_name> <attribute_name> <value>"
#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);