This week I went on a fascinating odyssey into Zephyr’s fuel gauge subsystem. This doesn’t refer to gasoline (or petrol as my more refined colleagues would say). It instead deals with the ubiquitous Lithium rechargeable cell. There’s a lot that goes into monitoring and charging these batteries, so it’s not surprising that the silicon industry has a class of chips known as fuel gauge ICs. They automatically handle tasks like calculating percentage of battery capacity, run-time until empty, charge-time until full, current/voltage measurement, and battery cut-off. If you haven’t looked at these chips, you should!
Batteries are the future and Zephyr is ready for it with a number of drivers already in-tree. The drivers are pretty user-friendly, but what’s built into Zephyr does fall into two different driver categories. Let’s get plugged in and see what the fuel gauge is all about.
Is it a Fuel Gauge or a Sensor?
I’m not embarrassed to admit I lost a bunch of time trying to figure out why my Maxim max17262 fuel gauge IC has Zephyr support but doesn’t work with the fuel gauge subsystem. It turns out that some fuel gauge ICs have sensor drivers, while others have fuel gauge drivers. Don’t worry, you can get nearly the same data from either, but you need to use the one supported by your device.
How can you tell what kind of support exists for your chip? Head over to the Zephyr bindings index and look it up. On the face of it you can see several of the max17xxx chips have zephyr support.
But when we click on the links and drill down to the details for each of these drivers, we find that the max17048 has fuel gauge subsystem support while the max17262 has sensor subsystem support.
Different drivers have Kconfig and API names that are… different. But the approach to working with these chips will be largely the same. You get an instance of the chip out of devicetree, run a fetch command to populate a struct with values read from the chip, then utilize those values.
Using the Fuel Gauge Subsystem
I don’t have a chip supported by the fuel gauge subsystem but I did take a thorough look into the drivers, including the sample app for the max17048. This driver has very few devicetree options available:
max17048:max17048@36 { compatible = "maxim,max17048"; status = "ok"; reg = <0x36 >; };
Enable the library using Kconfig:
CONFIG_FUEL_GAUGE=y
Access the device readings in c using a typical Zephyr pattern:
const struct device *const dev = DEVICE_DT_GET_ANY(maxim_max17048); union fuel_gauge_prop_val batt_val; int ret = fuel_gauge_get_prop(dev, FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE, &batt_val);
Your battery percentage will now be stored in batt_val.relative_state_of_charge
. The sample code shows four different properties being read from the max17048 (which is all this particular driver support). The subsystem defines numerous properties, accessible depending on the driver for your specific chip.
Using the Sensor Subsystem
The Golioth Elixir has a max17262 fuel gauge IC on the board which has Zephyr support via the sensory subsystem. Zephyr does include a sample app for reading from this chip. Despite not being directly included in the fuel gauge subsystem, this chip has for reading 14 properties (significantly more than the first driver we looked at).
The devicetree binding for this chip includes a number of useful properties so that device readings are properly associated with the physical parameters of battery you are using.
max17262@36 { compatible = "maxim,max17262"; reg = <0x36>; design-voltage = <4200>; desired-voltage = <3700>; desired-charging-current = <800>; design-cap = <850>; empty-voltage = <3300>; recovery-voltage = <3880>; charge-voltage = <4200>; status = "okay"; };
Enable the library using Kconfig:
CONFIG_SENSOR=y
Access the device readings in c using a typical Zephyr pattern:
static const struct device *const dev = DEVICE_DT_GET_ONE(maxim_max17262); struct sensor_value batt_pct; sensor_sample_fetch(dev); /* gather all sensor values */ sensor_channel_get(dev, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE, &batt_pct);
Your battery percentage will now be stored in batt_pct
. I’ll leave it up to you to explore all the other properties you can read form this device.
Useful Chips with Useful Driver
Adding a fuel gauge IC to your battery-powered designs offloads almost all effort when it comes to taking reliable battery readings and estimating charge and drain times. Zephyr’s support for these values makes using them a snap when the driver is already in-tree. But even if there is no existing driver, the infrastructure greatly lessens the burden of adding your own.
No comments yet! Start the discussion at forum.golioth.io