#![no_std]
#![deny(missing_docs)]
#![deny(warnings)]
#![allow(clippy::missing_errors_doc)]
use core::fmt::Debug;
#[cfg(feature = "async")]
use device_driver::AsyncRegisterInterface;
use device_driver::RegisterInterface;
use embedded_hal as hal;
#[cfg(feature = "async")]
use embedded_hal_async as hal_async;
#[allow(unsafe_code)]
#[allow(missing_docs)]
#[allow(clippy::doc_markdown, clippy::missing_errors_doc, clippy::identity_op)]
mod generated {
device_driver::create_device!(
device_name: Bq27441Device,
manifest: "src/bq27441.yaml"
);
}
pub use generated::{Bq27441Device, field_sets};
pub const DEFAULT_I2C_ADDRESS: u8 = 0x55;
pub const UNSEAL_KEY: u16 = 0x8000;
pub const DEVICE_TYPE_ID: u16 = 0x0421;
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ControlCmd {
ControlStatus = 0x0000,
DeviceType = 0x0001,
FwVersion = 0x0002,
DmCode = 0x0004,
PrevMacWrite = 0x0007,
ChemId = 0x0008,
BatInsert = 0x000C,
BatRemove = 0x000D,
SetHibernate = 0x0011,
ClearHibernate = 0x0012,
SetCfgUpdate = 0x0013,
ShutdownEnable = 0x001B,
Shutdown = 0x001C,
Sealed = 0x0020,
PulseGpout = 0x0023,
Reset = 0x0041,
SoftReset = 0x0042,
ExitCfgUpdate = 0x0043,
ExitResim = 0x0044,
}
#[derive(Debug)]
pub enum Error<E> {
I2c(E),
InvalidDevice,
InvalidParam,
}
impl<E> From<E> for Error<E> {
fn from(err: E) -> Self {
Error::I2c(err)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ChemId {
G1A = 0x0128,
G1B = 0x0312,
}
pub struct DeviceInterface<I2C> {
pub i2c: I2C,
pub address: u8,
}
#[cfg(feature = "async")]
pub struct DeviceInterfaceAsync<I2C> {
pub i2c: I2C,
pub address: u8,
}
impl<I2C> RegisterInterface for DeviceInterface<I2C>
where
I2C: hal::i2c::I2c,
{
type Error = I2C::Error;
type AddressType = u8;
fn write_register(&mut self, address: Self::AddressType, _size_bits: u32, data: &[u8]) -> Result<(), Self::Error> {
let mut buf = [0u8; 1 + 8];
buf[0] = address;
let end = 1 + data.len();
buf[1..end].copy_from_slice(data);
self.i2c.write(self.address, &buf[..end])
}
fn read_register(
&mut self,
address: Self::AddressType,
_size_bits: u32,
data: &mut [u8],
) -> Result<(), Self::Error> {
self.i2c.write_read(self.address, &[address], data)
}
}
#[cfg(feature = "async")]
impl<I2C> AsyncRegisterInterface for DeviceInterfaceAsync<I2C>
where
I2C: hal_async::i2c::I2c,
{
type Error = I2C::Error;
type AddressType = u8;
async fn write_register(
&mut self,
address: Self::AddressType,
_size_bits: u32,
data: &[u8],
) -> Result<(), Self::Error> {
let mut buf = [0u8; 1 + 8];
buf[0] = address;
let end = 1 + data.len();
buf[1..end].copy_from_slice(data);
self.i2c.write(self.address, &buf[..end]).await
}
async fn read_register(
&mut self,
address: Self::AddressType,
_size_bits: u32,
data: &mut [u8],
) -> Result<(), Self::Error> {
self.i2c.write_read(self.address, &[address], data).await
}
}
pub struct Bq27441<I2C> {
device: Bq27441Device<DeviceInterface<I2C>>,
}
impl<I2C> Bq27441<I2C>
where
I2C: hal::i2c::I2c,
I2C::Error: Debug,
{
pub fn new(i2c: I2C) -> Result<Self, Error<I2C::Error>> {
Self::new_with_address(i2c, DEFAULT_I2C_ADDRESS)
}
pub fn new_with_address(i2c: I2C, address: u8) -> Result<Self, Error<I2C::Error>> {
let interface = DeviceInterface { i2c, address };
let device = Bq27441Device::new(interface);
let mut this = Self { device };
this.verify_device()?;
Ok(this)
}
fn verify_device(&mut self) -> Result<(), Error<I2C::Error>> {
let device_type = self.control_read(ControlCmd::DeviceType)?;
if device_type != DEVICE_TYPE_ID {
return Err(Error::InvalidDevice);
}
Ok(())
}
pub fn control_read(&mut self, cmd: ControlCmd) -> Result<u16, Error<I2C::Error>> {
let cmd_bytes = (cmd as u16).to_le_bytes();
self.device.control()
.write(|w| *w = field_sets::Control::from(cmd_bytes))
.map_err(Error::I2c)?;
let result = self.device.control().read().map_err(Error::I2c)?;
let bytes: [u8; 2] = result.into();
Ok(u16::from_le_bytes(bytes))
}
pub fn control_write(&mut self, cmd: ControlCmd) -> Result<(), Error<I2C::Error>> {
let cmd_bytes = (cmd as u16).to_le_bytes();
self.device.control()
.write(|w| *w = field_sets::Control::from(cmd_bytes))
.map_err(Error::I2c)
}
pub fn voltage(&mut self) -> Result<u16, Error<I2C::Error>> {
let val = self.device.voltage().read().map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(u16::from_le_bytes(bytes))
}
pub fn temperature_raw(&mut self) -> Result<u16, Error<I2C::Error>> {
let val = self.device.temperature().read().map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(u16::from_le_bytes(bytes))
}
pub fn temperature_celsius(&mut self) -> Result<f32, Error<I2C::Error>> {
let raw = self.temperature_raw()?;
Ok((raw as f32 * 0.1) - 273.15)
}
pub fn state_of_charge(&mut self) -> Result<u16, Error<I2C::Error>> {
let val = self.device.state_of_charge().read().map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(u16::from_le_bytes(bytes))
}
pub fn remaining_capacity(&mut self) -> Result<u16, Error<I2C::Error>> {
let val = self.device.remaining_capacity().read().map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(u16::from_le_bytes(bytes))
}
pub fn full_charge_capacity(&mut self) -> Result<u16, Error<I2C::Error>> {
let val = self.device.full_charge_capacity().read().map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(u16::from_le_bytes(bytes))
}
pub fn average_current(&mut self) -> Result<i16, Error<I2C::Error>> {
let val = self.device.average_current().read().map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(i16::from_le_bytes(bytes))
}
pub fn average_power(&mut self) -> Result<i16, Error<I2C::Error>> {
let val = self.device.average_power().read().map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(i16::from_le_bytes(bytes))
}
pub fn state_of_health(&mut self) -> Result<u8, Error<I2C::Error>> {
let val = self.device.state_of_health().read().map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(bytes[0]) }
pub fn flags(&mut self) -> Result<field_sets::Flags, Error<I2C::Error>> {
self.device.flags().read().map_err(Error::I2c)
}
pub fn is_battery_detected(&mut self) -> Result<bool, Error<I2C::Error>> {
let flags = self.flags()?;
Ok(flags.bat_det())
}
pub fn is_charging(&mut self) -> Result<bool, Error<I2C::Error>> {
let flags = self.flags()?;
Ok(flags.chg())
}
pub fn is_discharging(&mut self) -> Result<bool, Error<I2C::Error>> {
let flags = self.flags()?;
Ok(flags.dsg())
}
pub fn is_full_charged(&mut self) -> Result<bool, Error<I2C::Error>> {
let flags = self.flags()?;
Ok(flags.fc())
}
pub fn firmware_version(&mut self) -> Result<u16, Error<I2C::Error>> {
self.control_read(ControlCmd::FwVersion)
}
pub fn chemistry_id(&mut self) -> Result<u16, Error<I2C::Error>> {
self.control_read(ControlCmd::ChemId)
}
pub fn seal(&mut self) -> Result<(), Error<I2C::Error>> {
self.control_write(ControlCmd::Sealed)
}
pub fn unseal(&mut self) -> Result<(), Error<I2C::Error>> {
let key_bytes = UNSEAL_KEY.to_le_bytes();
self.device.control()
.write(|w| *w = field_sets::Control::from(key_bytes))
.map_err(Error::I2c)?;
self.device.control()
.write(|w| *w = field_sets::Control::from(key_bytes))
.map_err(Error::I2c)
}
pub fn enter_config_mode(&mut self) -> Result<(), Error<I2C::Error>> {
self.control_write(ControlCmd::SetCfgUpdate)
}
pub fn exit_config_mode(&mut self) -> Result<(), Error<I2C::Error>> {
self.control_write(ControlCmd::SoftReset)
}
pub fn soft_reset(&mut self) -> Result<(), Error<I2C::Error>> {
self.control_write(ControlCmd::SoftReset)
}
pub fn set_hibernate(&mut self) -> Result<(), Error<I2C::Error>> {
self.control_write(ControlCmd::SetHibernate)
}
pub fn clear_hibernate(&mut self) -> Result<(), Error<I2C::Error>> {
self.control_write(ControlCmd::ClearHibernate)
}
pub fn device(&mut self) -> &mut Bq27441Device<DeviceInterface<I2C>> {
&mut self.device
}
pub fn destroy(self) -> I2C {
self.device.interface.i2c
}
}
#[cfg(feature = "async")]
pub struct Bq27441Async<I2C> {
device: Bq27441Device<DeviceInterfaceAsync<I2C>>,
}
#[cfg(feature = "async")]
impl<I2C> Bq27441Async<I2C>
where
I2C: hal_async::i2c::I2c,
I2C::Error: Debug,
{
pub async fn new(i2c: I2C) -> Result<Self, Error<I2C::Error>> {
Self::new_with_address(i2c, DEFAULT_I2C_ADDRESS).await
}
pub async fn new_with_address(i2c: I2C, address: u8) -> Result<Self, Error<I2C::Error>> {
let interface = DeviceInterfaceAsync { i2c, address };
let device = Bq27441Device::new(interface);
let mut this = Self { device };
this.verify_device().await?;
Ok(this)
}
async fn verify_device(&mut self) -> Result<(), Error<I2C::Error>> {
let device_type = self.control_read(ControlCmd::DeviceType).await?;
if device_type != DEVICE_TYPE_ID {
return Err(Error::InvalidDevice);
}
Ok(())
}
pub async fn control_read(&mut self, cmd: ControlCmd) -> Result<u16, Error<I2C::Error>> {
let cmd_bytes = (cmd as u16).to_le_bytes();
self.device.control()
.write_async(|w| *w = field_sets::Control::from(cmd_bytes))
.await
.map_err(Error::I2c)?;
let result = self.device.control().read_async().await.map_err(Error::I2c)?;
let bytes: [u8; 2] = result.into();
Ok(u16::from_le_bytes(bytes))
}
pub async fn control_write(&mut self, cmd: ControlCmd) -> Result<(), Error<I2C::Error>> {
let cmd_bytes = (cmd as u16).to_le_bytes();
self.device.control()
.write_async(|w| *w = field_sets::Control::from(cmd_bytes))
.await
.map_err(Error::I2c)
}
pub async fn voltage(&mut self) -> Result<u16, Error<I2C::Error>> {
let val = self.device.voltage().read_async().await.map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(u16::from_le_bytes(bytes))
}
pub async fn temperature_raw(&mut self) -> Result<u16, Error<I2C::Error>> {
let val = self.device.temperature().read_async().await.map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(u16::from_le_bytes(bytes))
}
pub async fn temperature_celsius(&mut self) -> Result<f32, Error<I2C::Error>> {
let raw = self.temperature_raw().await?;
Ok((raw as f32 * 0.1) - 273.15)
}
pub async fn state_of_charge(&mut self) -> Result<u16, Error<I2C::Error>> {
let val = self.device.state_of_charge().read_async().await.map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(u16::from_le_bytes(bytes))
}
pub async fn remaining_capacity(&mut self) -> Result<u16, Error<I2C::Error>> {
let val = self.device.remaining_capacity().read_async().await.map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(u16::from_le_bytes(bytes))
}
pub async fn full_charge_capacity(&mut self) -> Result<u16, Error<I2C::Error>> {
let val = self.device.full_charge_capacity().read_async().await.map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(u16::from_le_bytes(bytes))
}
pub async fn average_current(&mut self) -> Result<i16, Error<I2C::Error>> {
let val = self.device.average_current().read_async().await.map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(i16::from_le_bytes(bytes))
}
pub async fn average_power(&mut self) -> Result<i16, Error<I2C::Error>> {
let val = self.device.average_power().read_async().await.map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(i16::from_le_bytes(bytes))
}
pub async fn state_of_health(&mut self) -> Result<u8, Error<I2C::Error>> {
let val = self.device.state_of_health().read_async().await.map_err(Error::I2c)?;
let bytes: [u8; 2] = val.into();
Ok(bytes[0])
}
pub async fn flags(&mut self) -> Result<field_sets::Flags, Error<I2C::Error>> {
self.device.flags().read_async().await.map_err(Error::I2c)
}
pub async fn is_battery_detected(&mut self) -> Result<bool, Error<I2C::Error>> {
let flags = self.flags().await?;
Ok(flags.bat_det())
}
pub async fn is_charging(&mut self) -> Result<bool, Error<I2C::Error>> {
let flags = self.flags().await?;
Ok(flags.chg())
}
pub async fn is_discharging(&mut self) -> Result<bool, Error<I2C::Error>> {
let flags = self.flags().await?;
Ok(flags.dsg())
}
pub async fn is_full_charged(&mut self) -> Result<bool, Error<I2C::Error>> {
let flags = self.flags().await?;
Ok(flags.fc())
}
pub async fn firmware_version(&mut self) -> Result<u16, Error<I2C::Error>> {
self.control_read(ControlCmd::FwVersion).await
}
pub async fn chemistry_id(&mut self) -> Result<u16, Error<I2C::Error>> {
self.control_read(ControlCmd::ChemId).await
}
pub async fn seal(&mut self) -> Result<(), Error<I2C::Error>> {
self.control_write(ControlCmd::Sealed).await
}
pub async fn unseal(&mut self) -> Result<(), Error<I2C::Error>> {
let key_bytes = UNSEAL_KEY.to_le_bytes();
self.device.control()
.write_async(|w| *w = field_sets::Control::from(key_bytes))
.await
.map_err(Error::I2c)?;
self.device.control()
.write_async(|w| *w = field_sets::Control::from(key_bytes))
.await
.map_err(Error::I2c)
}
pub async fn enter_config_mode(&mut self) -> Result<(), Error<I2C::Error>> {
self.control_write(ControlCmd::SetCfgUpdate).await
}
pub async fn exit_config_mode(&mut self) -> Result<(), Error<I2C::Error>> {
self.control_write(ControlCmd::SoftReset).await
}
pub async fn soft_reset(&mut self) -> Result<(), Error<I2C::Error>> {
self.control_write(ControlCmd::SoftReset).await
}
pub async fn set_hibernate(&mut self) -> Result<(), Error<I2C::Error>> {
self.control_write(ControlCmd::SetHibernate).await
}
pub async fn clear_hibernate(&mut self) -> Result<(), Error<I2C::Error>> {
self.control_write(ControlCmd::ClearHibernate).await
}
pub fn device(&mut self) -> &mut Bq27441Device<DeviceInterfaceAsync<I2C>> {
&mut self.device
}
pub fn destroy(self) -> I2C {
self.device.interface.i2c
}
}