1 /*
2  * Copyright (c) 2023 Alvaro Garcia Gomez <maxpowel@gmail.com>
3  * Copyright (c) 2025 Philipp Steiner <philipp.steiner1987@gmail.com>
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include "zephyr/sys/util.h"
9 #include <zephyr/kernel.h>
10 #include <zephyr/device.h>
11 #include <zephyr/devicetree.h>
12 #include <zephyr/drivers/fuel_gauge.h>
13 
14 #define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(app);
17 
fuel_gauge_prop_to_str(enum fuel_gauge_prop_type prop)18 const char *fuel_gauge_prop_to_str(enum fuel_gauge_prop_type prop)
19 {
20 	switch (prop) {
21 	case FUEL_GAUGE_AVG_CURRENT:
22 		return "FUEL_GAUGE_AVG_CURRENT";
23 	case FUEL_GAUGE_CURRENT:
24 		return "FUEL_GAUGE_CURRENT";
25 	case FUEL_GAUGE_CHARGE_CUTOFF:
26 		return "FUEL_GAUGE_CHARGE_CUTOFF";
27 	case FUEL_GAUGE_CYCLE_COUNT:
28 		return "FUEL_GAUGE_CYCLE_COUNT";
29 	case FUEL_GAUGE_CONNECT_STATE:
30 		return "FUEL_GAUGE_CONNECT_STATE";
31 	case FUEL_GAUGE_FLAGS:
32 		return "FUEL_GAUGE_FLAGS";
33 	case FUEL_GAUGE_FULL_CHARGE_CAPACITY:
34 		return "FUEL_GAUGE_FULL_CHARGE_CAPACITY";
35 	case FUEL_GAUGE_PRESENT_STATE:
36 		return "FUEL_GAUGE_PRESENT_STATE";
37 	case FUEL_GAUGE_REMAINING_CAPACITY:
38 		return "FUEL_GAUGE_REMAINING_CAPACITY";
39 	case FUEL_GAUGE_RUNTIME_TO_EMPTY:
40 		return "FUEL_GAUGE_RUNTIME_TO_EMPTY";
41 	case FUEL_GAUGE_RUNTIME_TO_FULL:
42 		return "FUEL_GAUGE_RUNTIME_TO_FULL";
43 	case FUEL_GAUGE_SBS_MFR_ACCESS:
44 		return "FUEL_GAUGE_SBS_MFR_ACCESS";
45 	case FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE:
46 		return "FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE";
47 	case FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE:
48 		return "FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE";
49 	case FUEL_GAUGE_TEMPERATURE:
50 		return "FUEL_GAUGE_TEMPERATURE";
51 	case FUEL_GAUGE_VOLTAGE:
52 		return "FUEL_GAUGE_VOLTAGE";
53 	case FUEL_GAUGE_SBS_MODE:
54 		return "FUEL_GAUGE_SBS_MODE";
55 	case FUEL_GAUGE_CHARGE_CURRENT:
56 		return "FUEL_GAUGE_CHARGE_CURRENT";
57 	case FUEL_GAUGE_CHARGE_VOLTAGE:
58 		return "FUEL_GAUGE_CHARGE_VOLTAGE";
59 	case FUEL_GAUGE_STATUS:
60 		return "FUEL_GAUGE_STATUS";
61 	case FUEL_GAUGE_DESIGN_CAPACITY:
62 		return "FUEL_GAUGE_DESIGN_CAPACITY";
63 	case FUEL_GAUGE_DESIGN_VOLTAGE:
64 		return "FUEL_GAUGE_DESIGN_VOLTAGE";
65 	case FUEL_GAUGE_SBS_ATRATE:
66 		return "FUEL_GAUGE_SBS_ATRATE";
67 	case FUEL_GAUGE_SBS_ATRATE_TIME_TO_FULL:
68 		return "FUEL_GAUGE_SBS_ATRATE_TIME_TO_FULL";
69 	case FUEL_GAUGE_SBS_ATRATE_TIME_TO_EMPTY:
70 		return "FUEL_GAUGE_SBS_ATRATE_TIME_TO_EMPTY";
71 	case FUEL_GAUGE_SBS_ATRATE_OK:
72 		return "FUEL_GAUGE_SBS_ATRATE_OK";
73 	case FUEL_GAUGE_SBS_REMAINING_CAPACITY_ALARM:
74 		return "FUEL_GAUGE_SBS_REMAINING_CAPACITY_ALARM";
75 	case FUEL_GAUGE_SBS_REMAINING_TIME_ALARM:
76 		return "FUEL_GAUGE_SBS_REMAINING_TIME_ALARM";
77 	case FUEL_GAUGE_MANUFACTURER_NAME:
78 		return "FUEL_GAUGE_MANUFACTURER_NAME";
79 	case FUEL_GAUGE_DEVICE_NAME:
80 		return "FUEL_GAUGE_DEVICE_NAME";
81 	case FUEL_GAUGE_DEVICE_CHEMISTRY:
82 		return "FUEL_GAUGE_DEVICE_CHEMISTRY";
83 	case FUEL_GAUGE_CURRENT_DIRECTION:
84 		return "FUEL_GAUGE_CURRENT_DIRECTION";
85 	case FUEL_GAUGE_STATE_OF_CHARGE_ALARM:
86 		return "FUEL_GAUGE_STATE_OF_CHARGE_ALARM";
87 	case FUEL_GAUGE_LOW_VOLTAGE_ALARM:
88 		return "FUEL_GAUGE_LOW_VOLTAGE_ALARM";
89 	default:
90 		return "Unknown fuel gauge property";
91 	}
92 }
93 
main(void)94 int main(void)
95 {
96 	const struct device *const dev = DEVICE_DT_GET(DT_ALIAS(fuel_gauge0));
97 	int ret = 0;
98 
99 	if (dev == NULL) {
100 		LOG_ERR("no device found.");
101 		return 0;
102 	}
103 
104 	if (!device_is_ready(dev)) {
105 		LOG_ERR("Error: Device \"%s\" is not ready; check the driver initialization logs "
106 			"for errors.",
107 			dev->name);
108 		return 0;
109 	}
110 
111 	LOG_INF("Found device \"%s\"", dev->name);
112 
113 	{
114 		LOG_INF("Test-Read generic fuel gauge properties to verify which are supported");
115 		LOG_INF("Info: not all properties are supported by all fuel gauges!");
116 
117 		fuel_gauge_prop_t test_props[] = {
118 			FUEL_GAUGE_AVG_CURRENT,
119 			FUEL_GAUGE_CURRENT,
120 			FUEL_GAUGE_CHARGE_CUTOFF,
121 			FUEL_GAUGE_CYCLE_COUNT,
122 			FUEL_GAUGE_CONNECT_STATE,
123 			FUEL_GAUGE_FLAGS,
124 			FUEL_GAUGE_FULL_CHARGE_CAPACITY,
125 			FUEL_GAUGE_PRESENT_STATE,
126 			FUEL_GAUGE_REMAINING_CAPACITY,
127 			FUEL_GAUGE_RUNTIME_TO_EMPTY,
128 			FUEL_GAUGE_RUNTIME_TO_FULL,
129 			FUEL_GAUGE_SBS_MFR_ACCESS,
130 			FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE,
131 			FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE,
132 			FUEL_GAUGE_TEMPERATURE,
133 			FUEL_GAUGE_VOLTAGE,
134 			FUEL_GAUGE_SBS_MODE,
135 			FUEL_GAUGE_CHARGE_CURRENT,
136 			FUEL_GAUGE_CHARGE_VOLTAGE,
137 			FUEL_GAUGE_STATUS,
138 			FUEL_GAUGE_DESIGN_CAPACITY,
139 			FUEL_GAUGE_DESIGN_VOLTAGE,
140 			FUEL_GAUGE_SBS_ATRATE,
141 			FUEL_GAUGE_SBS_ATRATE_TIME_TO_FULL,
142 			FUEL_GAUGE_SBS_ATRATE_TIME_TO_EMPTY,
143 			FUEL_GAUGE_SBS_ATRATE_OK,
144 			FUEL_GAUGE_SBS_REMAINING_CAPACITY_ALARM,
145 			FUEL_GAUGE_SBS_REMAINING_TIME_ALARM,
146 			FUEL_GAUGE_MANUFACTURER_NAME,
147 			FUEL_GAUGE_DEVICE_NAME,
148 			FUEL_GAUGE_DEVICE_CHEMISTRY,
149 			FUEL_GAUGE_CURRENT_DIRECTION,
150 			FUEL_GAUGE_STATE_OF_CHARGE_ALARM,
151 			FUEL_GAUGE_LOW_VOLTAGE_ALARM,
152 		};
153 
154 		union fuel_gauge_prop_val test_vals[ARRAY_SIZE(test_props)];
155 
156 		for (size_t i = 0; i < ARRAY_SIZE(test_props); i++) {
157 
158 			if (test_props[i] == FUEL_GAUGE_MANUFACTURER_NAME) {
159 				struct sbs_gauge_manufacturer_name mfg_name;
160 
161 				ret = fuel_gauge_get_buffer_prop(dev, FUEL_GAUGE_MANUFACTURER_NAME,
162 								 &mfg_name, sizeof(mfg_name));
163 				if (ret == -ENOTSUP) {
164 					LOG_INF("Property \"%s\" is not supported",
165 						fuel_gauge_prop_to_str(test_props[i]));
166 				} else if (ret < 0) {
167 					LOG_ERR("Error: cannot get property \"%s\": %d",
168 						fuel_gauge_prop_to_str(test_props[i]), ret);
169 				} else {
170 					LOG_INF("Property \"%s\" is supported",
171 						fuel_gauge_prop_to_str(test_props[i]));
172 					LOG_INF("Manufacturer name: %s",
173 						mfg_name.manufacturer_name);
174 				}
175 			} else if (test_props[i] == FUEL_GAUGE_DEVICE_NAME) {
176 				struct sbs_gauge_device_name dev_name;
177 
178 				ret = fuel_gauge_get_buffer_prop(dev, FUEL_GAUGE_DEVICE_NAME,
179 								 &dev_name, sizeof(dev_name));
180 				if (ret == -ENOTSUP) {
181 					LOG_INF("Property \"%s\" is not supported",
182 						fuel_gauge_prop_to_str(test_props[i]));
183 				} else if (ret < 0) {
184 					LOG_ERR("Error: cannot get property \"%s\": %d",
185 						fuel_gauge_prop_to_str(test_props[i]), ret);
186 				} else {
187 					LOG_INF("Property \"%s\" is supported",
188 						fuel_gauge_prop_to_str(test_props[i]));
189 					LOG_INF("Device name: %s", dev_name.device_name);
190 				}
191 			} else if (test_props[i] == FUEL_GAUGE_DEVICE_CHEMISTRY) {
192 				struct sbs_gauge_device_chemistry device_chemistry;
193 
194 				ret = fuel_gauge_get_buffer_prop(dev, FUEL_GAUGE_DEVICE_CHEMISTRY,
195 								 &device_chemistry,
196 								 sizeof(device_chemistry));
197 				if (ret == -ENOTSUP) {
198 					LOG_INF("Property \"%s\" is not supported",
199 						fuel_gauge_prop_to_str(test_props[i]));
200 				} else if (ret < 0) {
201 					LOG_ERR("Error: cannot get property \"%s\": %d",
202 						fuel_gauge_prop_to_str(test_props[i]), ret);
203 				} else {
204 					LOG_INF("Property \"%s\" is supported",
205 						fuel_gauge_prop_to_str(test_props[i]));
206 					LOG_INF("Device chemistry: %s",
207 						device_chemistry.device_chemistry);
208 				}
209 			} else {
210 				/* For all other properties, use the generic get_props function */
211 
212 				ret = fuel_gauge_get_props(dev, &test_props[i], &test_vals[i], 1);
213 
214 				if (ret == -ENOTSUP) {
215 					LOG_INF("Property \"%s\" is not supported",
216 						fuel_gauge_prop_to_str(test_props[i]));
217 				} else if (ret < 0) {
218 					LOG_ERR("Error: cannot get property \"%s\": %d",
219 						fuel_gauge_prop_to_str(test_props[i]), ret);
220 				} else {
221 					LOG_INF("Property \"%s\" is supported",
222 						fuel_gauge_prop_to_str(test_props[i]));
223 
224 					switch (test_props[i]) {
225 					case FUEL_GAUGE_AVG_CURRENT:
226 						LOG_INF("  Avg current: %d",
227 							test_vals[i].avg_current);
228 						break;
229 					case FUEL_GAUGE_CURRENT:
230 						LOG_INF("  Current: %d", test_vals[i].current);
231 						break;
232 					case FUEL_GAUGE_CYCLE_COUNT:
233 						LOG_INF("  Cycle count: %" PRIu32,
234 							test_vals[i].cycle_count);
235 						break;
236 					case FUEL_GAUGE_CONNECT_STATE:
237 						LOG_INF("  Connect state: 0x%" PRIx32,
238 							test_vals[i].connect_state);
239 						break;
240 					case FUEL_GAUGE_FLAGS:
241 						LOG_INF("  Flags: 0x%" PRIx32, test_vals[i].flags);
242 						break;
243 					case FUEL_GAUGE_FULL_CHARGE_CAPACITY:
244 						LOG_INF("  Full charge capacity: %" PRIu32,
245 							test_vals[i].full_charge_capacity);
246 						break;
247 					case FUEL_GAUGE_PRESENT_STATE:
248 						LOG_INF("  Present state: %d",
249 							test_vals[i].present_state);
250 						break;
251 					case FUEL_GAUGE_REMAINING_CAPACITY:
252 						LOG_INF("  Remaining capacity: %" PRIu32,
253 							test_vals[i].remaining_capacity);
254 						break;
255 					case FUEL_GAUGE_RUNTIME_TO_EMPTY:
256 						LOG_INF("  Runtime to empty: %" PRIu32,
257 							test_vals[i].runtime_to_empty);
258 						break;
259 					case FUEL_GAUGE_RUNTIME_TO_FULL:
260 						LOG_INF("  Runtime to full: %" PRIu32,
261 							test_vals[i].runtime_to_full);
262 						break;
263 					case FUEL_GAUGE_SBS_MFR_ACCESS:
264 						LOG_INF("  SBS MFR access: %" PRIu16,
265 							test_vals[i].sbs_mfr_access_word);
266 						break;
267 					case FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE:
268 						LOG_INF("  Absolute state of charge: %" PRIu8,
269 							test_vals[i].absolute_state_of_charge);
270 						break;
271 					case FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE:
272 						LOG_INF("  Relative state of charge: %" PRIu8,
273 							test_vals[i].relative_state_of_charge);
274 						break;
275 					case FUEL_GAUGE_TEMPERATURE:
276 						LOG_INF("  Temperature: %" PRIu16,
277 							test_vals[i].temperature);
278 						break;
279 					case FUEL_GAUGE_VOLTAGE:
280 						LOG_INF("  Voltage: %d", test_vals[i].voltage);
281 						break;
282 					case FUEL_GAUGE_SBS_MODE:
283 						LOG_INF("  SBS mode: %" PRIu16,
284 							test_vals[i].sbs_mode);
285 						break;
286 					case FUEL_GAUGE_CHARGE_CURRENT:
287 						LOG_INF("  Charge current: %" PRIu32,
288 							test_vals[i].chg_current);
289 						break;
290 					case FUEL_GAUGE_CHARGE_VOLTAGE:
291 						LOG_INF("  Charge voltage: %" PRIu32,
292 							test_vals[i].chg_voltage);
293 						break;
294 					case FUEL_GAUGE_STATUS:
295 						LOG_INF("  Status: 0x%" PRIx16,
296 							test_vals[i].fg_status);
297 						break;
298 					case FUEL_GAUGE_DESIGN_CAPACITY:
299 						LOG_INF("  Design capacity: %" PRIu16,
300 							test_vals[i].design_cap);
301 						break;
302 					case FUEL_GAUGE_DESIGN_VOLTAGE:
303 						LOG_INF("  Design voltage: %" PRIx16,
304 							test_vals[i].design_volt);
305 						break;
306 					case FUEL_GAUGE_SBS_ATRATE:
307 						LOG_INF("  SBS at rate: %" PRIi16,
308 							test_vals[i].sbs_at_rate);
309 						break;
310 					case FUEL_GAUGE_SBS_ATRATE_TIME_TO_FULL:
311 						LOG_INF("  SBS at rate time to full: %" PRIu16,
312 							test_vals[i].sbs_at_rate_time_to_full);
313 						break;
314 					case FUEL_GAUGE_SBS_ATRATE_TIME_TO_EMPTY:
315 						LOG_INF("  SBS at rate time to empty: %" PRIu16,
316 							test_vals[i].sbs_at_rate_time_to_empty);
317 						break;
318 					case FUEL_GAUGE_SBS_ATRATE_OK:
319 						LOG_INF("  SBS at rate ok: %d",
320 							test_vals[i].sbs_at_rate_ok);
321 						break;
322 					case FUEL_GAUGE_SBS_REMAINING_CAPACITY_ALARM:
323 						LOG_INF("  SBS remaining capacity alarm: %" PRIu16,
324 							test_vals[i].sbs_remaining_capacity_alarm);
325 						break;
326 					case FUEL_GAUGE_SBS_REMAINING_TIME_ALARM:
327 						LOG_INF("  SBS remaining time alarm: %" PRIu16,
328 							test_vals[i].sbs_remaining_time_alarm);
329 						break;
330 					case FUEL_GAUGE_CURRENT_DIRECTION:
331 						LOG_INF("  Current direction: %" PRIu16,
332 							test_vals[i].current_direction);
333 						break;
334 					case FUEL_GAUGE_STATE_OF_CHARGE_ALARM:
335 						LOG_INF("  State of charge alarm: %" PRIu8,
336 							test_vals[i].state_of_charge_alarm);
337 						break;
338 					case FUEL_GAUGE_LOW_VOLTAGE_ALARM:
339 						LOG_INF("  Low voltage alarm: %" PRIu32,
340 							test_vals[i].low_voltage_alarm);
341 						break;
342 					}
343 				}
344 			}
345 		}
346 	}
347 
348 	LOG_INF("Polling fuel gauge data 'FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE' & "
349 		"'FUEL_GAUGE_VOLTAGE'");
350 
351 	while (1) {
352 		fuel_gauge_prop_t poll_props[] = {
353 			FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE,
354 			FUEL_GAUGE_VOLTAGE,
355 		};
356 
357 		union fuel_gauge_prop_val poll_vals[ARRAY_SIZE(poll_props)];
358 
359 		ret = fuel_gauge_get_props(dev, poll_props, poll_vals, ARRAY_SIZE(poll_props));
360 
361 		if (ret < 0) {
362 			LOG_ERR("Error: cannot get properties");
363 		} else {
364 			LOG_INF("Fuel gauge data: Charge: %d%%, Voltage: %dmV",
365 				poll_vals[0].relative_state_of_charge, poll_vals[1].voltage / 1000);
366 		}
367 
368 		k_sleep(K_MSEC(5000));
369 	}
370 	return 0;
371 }
372