waveshare_ups_hat_e/
lib.rs1#![doc = include_str!("../README.md")]
5
6pub mod error;
7pub mod registers;
8
9use error::Error;
10use i2cdev::core::I2CDevice;
11use i2cdev::linux::LinuxI2CDevice;
12use registers::{
13 BATTERY_REG, CELL_VOLTAGE_REG, CHARGING_REG, COMMUNICATION_REG, ChargerActivity, ChargingState,
14 CommState, POWEROFF_REG, USBC_VBUS_REG, UsbCInputState, UsbCPowerDelivery,
15};
16use crate::registers::SOFTWARE_REV_REG;
17
18pub const DEFAULT_I2C_ADDRESS: u16 = 0x2d;
20
21pub const DEFAULT_I2C_DEV_PATH: &str = "/dev/i2c-1";
23
24pub const DEFAULT_CELL_LOW_VOLTAGE_THRESHOLD: u16 = 3400; pub const POWEROFF_VALUE: u8 = 0x55;
32
33#[derive(Debug)]
35pub struct PowerState {
36 pub charging_state: ChargingState,
37 pub charger_activity: ChargerActivity,
38 pub usbc_input_state: UsbCInputState,
39 pub usbc_power_delivery: UsbCPowerDelivery,
40}
41
42#[derive(Debug)]
45pub struct CommunicationState {
46 pub bq4050: CommState,
47 pub ip2368: CommState,
48}
49
50#[derive(Debug)]
58pub struct BatteryState {
59 pub millivolts: u16,
60 pub milliamps: i16,
61 pub remaining_percent: u16,
62 pub remaining_capacity_milliamphours: u16,
63 pub remaining_runtime_minutes: u16,
64 pub time_to_full_minutes: u16,
65}
66
67#[derive(Debug)]
69pub struct CellVoltage {
70 pub cell_1_millivolts: u16,
71 pub cell_2_millivolts: u16,
72 pub cell_3_millivolts: u16,
73 pub cell_4_millivolts: u16,
74}
75
76#[derive(Debug)]
78pub struct UsbCVBus {
79 pub millivolts: u16,
80 pub milliamps: u16,
81 pub milliwatts: u16,
82}
83
84pub struct UpsHatE {
90 i2c_bus: LinuxI2CDevice,
91}
92
93impl Default for UpsHatE {
94 fn default() -> Self {
97 let i2c = LinuxI2CDevice::new(DEFAULT_I2C_DEV_PATH, DEFAULT_I2C_ADDRESS)
98 .expect("Failed to open I2C device");
99
100 Self { i2c_bus: i2c }
101 }
102}
103
104impl UpsHatE {
105 pub fn new() -> Self {
108 Self::default()
109 }
110
111 pub fn from_i2c_device(i2c_bus: LinuxI2CDevice) -> Self {
114 Self { i2c_bus }
115 }
116
117 pub fn get_cell_voltage(&mut self) -> Result<CellVoltage, Error> {
118 let data = self.read_block(CELL_VOLTAGE_REG.id, CELL_VOLTAGE_REG.length)?;
119
120 let voltages = CellVoltage {
121 cell_1_millivolts: data[0] as u16 | (data[1] as u16) << 8,
122 cell_2_millivolts: data[2] as u16 | (data[3] as u16) << 8,
123 cell_3_millivolts: data[4] as u16 | (data[5] as u16) << 8,
124 cell_4_millivolts: data[6] as u16 | (data[7] as u16) << 8,
125 };
126
127 Ok(voltages)
128 }
129
130 pub fn get_usbc_vbus(&mut self) -> Result<UsbCVBus, Error> {
131 let data = self.read_block(USBC_VBUS_REG.id, USBC_VBUS_REG.length)?;
132
133 let vbus = UsbCVBus {
134 millivolts: data[0] as u16 | (data[1] as u16) << 8,
135 milliamps: data[2] as u16 | (data[3] as u16) << 8,
136 milliwatts: data[4] as u16 | (data[5] as u16) << 8,
137 };
138
139 Ok(vbus)
140 }
141
142 pub fn get_battery_state(&mut self) -> Result<BatteryState, Error> {
143 let data = self.read_block(BATTERY_REG.id, BATTERY_REG.length)?;
144
145 let milliamps: i16 = {
146 let mut current = data[2] as i32 | (data[3] as i32) << 8;
147 if current > 0x7fff {
149 current -= 0xffff;
150 }
151 current as i16
152 };
153
154 let mut remaining_runtime_minutes: u16 = 0;
155 let mut time_to_full_minutes: u16 = 0;
156
157 if milliamps < 0 {
158 remaining_runtime_minutes = data[8] as u16 | (data[9] as u16) << 8;
160 } else {
161 time_to_full_minutes = data[10] as u16 | (data[11] as u16) << 8;
163 }
164
165 let state = BatteryState {
166 millivolts: data[0] as u16 | (data[1] as u16) << 8,
167 milliamps,
168 remaining_percent: data[4] as u16 | (data[5] as u16) << 8,
169 remaining_capacity_milliamphours: data[6] as u16 | (data[7] as u16) << 8,
170 remaining_runtime_minutes,
171 time_to_full_minutes,
172 };
173
174 Ok(state)
175 }
176
177 pub fn get_power_state(&mut self) -> Result<PowerState, Error> {
178 let data = self.read_block(CHARGING_REG.id, CHARGING_REG.length)?;
179 let byte = data[0];
180
181 let charger_activity = ChargerActivity::try_from(byte & 0b111)?;
182 let usbc_input_state = UsbCInputState::from(byte & (1 << 5) != 0);
183 let usbc_power_delivery = UsbCPowerDelivery::from(byte & (1 << 6) != 0);
184 let charging_state = ChargingState::from(byte & (1 << 7) != 0);
185
186 Ok(PowerState {
187 charging_state,
188 charger_activity,
189 usbc_input_state,
190 usbc_power_delivery,
191 })
192 }
193
194 pub fn get_communication_state(&mut self) -> Result<CommunicationState, Error> {
195 let data = self.read_block(COMMUNICATION_REG.id, COMMUNICATION_REG.length)?;
196 let byte = data[0];
197
198 let ip2368 = CommState::from(byte & (1 << 0) != 0);
199 let bq4050 = CommState::from(byte & (1 << 1) != 0);
200
201 Ok(CommunicationState { bq4050, ip2368 })
202 }
203
204 pub fn get_software_revision(&mut self) -> Result<u8, Error> {
205 let data = self.read_block(SOFTWARE_REV_REG.id, SOFTWARE_REV_REG.length)?;
206 Ok(data[0])
207 }
208
209 #[allow(clippy::wrong_self_convention)]
214 pub fn is_battery_low(&mut self) -> Result<bool, Error> {
215 const CUTOFF: u32 = 4 * DEFAULT_CELL_LOW_VOLTAGE_THRESHOLD as u32;
216
217 let cell_voltages = self.get_cell_voltage()?;
218
219 let total_voltage: u32 = (cell_voltages.cell_1_millivolts
220 + cell_voltages.cell_2_millivolts
221 + cell_voltages.cell_3_millivolts
222 + cell_voltages.cell_4_millivolts) as u32;
223
224 Ok(total_voltage <= CUTOFF)
225 }
226
227 pub fn force_power_off(&mut self) -> Result<(), Error> {
231 self.i2c_bus
232 .smbus_write_byte_data(POWEROFF_REG.id, POWEROFF_VALUE)?;
233 Ok(())
234 }
235
236 #[allow(clippy::wrong_self_convention)]
238 pub fn is_power_off_pending(&mut self) -> Result<bool, Error> {
239 let data = self.read_block(POWEROFF_REG.id, POWEROFF_REG.length)?;
240 Ok(data[0] == POWEROFF_VALUE)
241 }
242
243 fn read_block(&mut self, register: u8, length: u8) -> Result<Vec<u8>, Error> {
244 let data = self.i2c_bus.smbus_read_i2c_block_data(register, length)?;
245
246 if data.len() != length as usize {
247 return Err(Error::InvalidDataLen(register, length as usize, data.len()));
248 }
249
250 Ok(data)
251 }
252}