1 /*
2  * Copyright 2020 Google LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/shell/shell.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/sensor.h>
13 
14 /**
15  * @brief Collect the values for several channels
16  *
17  * @param dev Sensor device to read from
18  * @param ... any number of pairs of arguments:
19  *	first is the sensor channel to read (-1 to terminate the list)
20  *	second is a pointer to the struct sensor_value to put it in
21  * @return 0 on success
22  * @return negative error code from sensor API on failure
23  */
get_channels(const struct device * dev,...)24 static int get_channels(const struct device *dev, ...)
25 {
26 	va_list ptr;
27 	int i;
28 
29 	va_start(ptr, dev);
30 	for (i = 0;; i++) {
31 		int chan;
32 		struct sensor_value *val;
33 		int err;
34 
35 		chan = va_arg(ptr, int);
36 		if (chan == -1) {
37 			break;
38 		}
39 		val = va_arg(ptr, struct sensor_value *);
40 		err = sensor_channel_get(dev, chan, val);
41 		if (err < 0) {
42 			va_end(ptr);
43 			return err;
44 		}
45 	}
46 
47 	va_end(ptr);
48 	return 0;
49 }
50 
51 /* battery */
cmd_battery(const struct shell * sh,size_t argc,char ** argv)52 static int cmd_battery(const struct shell *sh, size_t argc, char **argv)
53 {
54 	struct sensor_value temp, volt, current, i_desired, charge_remain;
55 	struct sensor_value charge, v_desired, v_design, cap, nom_cap;
56 	struct sensor_value full, empty;
57 	const struct device *const dev = DEVICE_DT_GET(DT_ALIAS(battery));
58 	bool allowed;
59 	int err;
60 
61 	if (!device_is_ready(dev)) {
62 		shell_error(sh, "Device not ready (%s)", argv[1]);
63 		return -ENODEV;
64 	}
65 
66 	err = sensor_sample_fetch(dev);
67 	if (err < 0) {
68 		shell_error(sh, "Failed to read sensor: %d", err);
69 	}
70 
71 	err = get_channels(dev,
72 			   SENSOR_CHAN_GAUGE_TEMP, &temp,
73 			   SENSOR_CHAN_GAUGE_VOLTAGE, &volt,
74 			   SENSOR_CHAN_GAUGE_AVG_CURRENT, &current,
75 			   SENSOR_CHAN_GAUGE_DESIRED_VOLTAGE, &v_desired,
76 			   SENSOR_CHAN_GAUGE_DESIRED_CHARGING_CURRENT,
77 			   &i_desired,
78 			   SENSOR_CHAN_GAUGE_STATE_OF_CHARGE, &charge,
79 			   SENSOR_CHAN_GAUGE_DESIGN_VOLTAGE, &v_design,
80 			   SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY,
81 			   &charge_remain,
82 			   SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY, &cap,
83 			   SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY, &nom_cap,
84 			   SENSOR_CHAN_GAUGE_TIME_TO_FULL, &full,
85 			   SENSOR_CHAN_GAUGE_TIME_TO_EMPTY, &empty,
86 			   -1);
87 	if (err < 0) {
88 		return err;
89 	}
90 
91 	shell_print(sh, "Temp:  %.1d.%02d C",
92 		    temp.val1, temp.val2 / 10000);
93 	shell_print(sh, "V: %5d.%02d V",
94 		    volt.val1, volt.val2 / 10000);
95 	shell_print(sh, "V-desired: %d.%02d V",
96 		    v_desired.val1, v_desired.val2 / 10000);
97 	shell_fprintf_normal(sh, "I:    %lld mA",
98 		    sensor_value_to_milli(&current));
99 	if (current.val1 > 0) {
100 		shell_fprintf_normal(sh, " (CHG)");
101 	} else if (current.val1 < 0) {
102 		shell_fprintf_normal(sh, " (DISCHG)");
103 	}
104 	shell_fprintf_normal(sh, "\n");
105 	shell_print(sh, "I-desired: %5d mA",
106 		    i_desired.val1);
107 	allowed = i_desired.val1 && v_desired.val2 && charge.val1 < 100;
108 	shell_print(sh, "Charging: %sAllowed",
109 		    allowed ? "" : "Not ");
110 	shell_print(sh, "Charge: %d %%", charge.val1);
111 	shell_print(sh, "V-design: %d.%02d V",
112 		    v_design.val1, v_design.val2 / 10000);
113 	shell_print(sh, "Remaining: %d mAh",
114 		    charge_remain.val1);
115 	shell_print(sh, "Cap-full: %d mAh", cap.val1);
116 	shell_print(sh, "Design: %d mAh", nom_cap.val1);
117 	shell_print(sh, "Time full: %dh:%02d",
118 		    full.val1 / 60, full.val1 % 60);
119 	shell_print(sh, "Time empty: %dh:%02d",
120 		    empty.val1 / 60, empty.val1 % 60);
121 
122 	return 0;
123 }
124 
125 SHELL_CMD_REGISTER(battery, NULL, "Battery status", cmd_battery);
126