diff --git a/doc/config.md b/doc/config.md index 1cebb1f..c8987a9 100644 --- a/doc/config.md +++ b/doc/config.md @@ -43,4 +43,7 @@ Json format configuration file of pisugar-server: default null soft_poweroff_shell Shell script of soft poweroff, default null - auto_rtc_sync Automatically sync rtc time (Every 10s) \ No newline at end of file + auto_rtc_sync Automatically sync rtc time (Every 10s) + + battery_curve Customized battery curve, optional, e.g.: + [[3.2, 5], [3.3, 20], [3.5, 60], [3.7, 80], [3.8, 90], [4.0, 100]] \ No newline at end of file diff --git a/pisugar-core/src/config.rs b/pisugar-core/src/config.rs new file mode 100644 index 0000000..775d462 --- /dev/null +++ b/pisugar-core/src/config.rs @@ -0,0 +1,201 @@ +use std::{ + fs::{File, OpenOptions}, + io::{self, Read, Write}, + path::Path, +}; + +use chrono::{DateTime, Local}; +use serde::{Deserialize, Serialize}; + +/// Battery voltage threshold, (low, percentage at low) +pub type BatteryThreshold = (f32, f32); + +fn default_i2c_bus() -> u8 { + 1 +} + +/// Default auth session timeout, 1h +fn default_session_timeout() -> u32 { + 60 * 60 +} + +/// PiSugar configuration +#[derive(Clone, Serialize, Deserialize)] +pub struct PiSugarConfig { + /// Http digest auth + #[serde(default)] + pub auth_user: Option, + + #[serde(default)] + pub auth_password: Option, + + /// Auth session timeout in seconds + #[serde(default = "default_session_timeout")] + pub session_timeout: u32, + + /// I2C bus, default 1 (/dev/i2c-1) + #[serde(default = "default_i2c_bus")] + pub i2c_bus: u8, + + /// I2C addr, default 0x57 (87), available in PiSugar3 + #[serde(default)] + pub i2c_addr: Option, + + /// Alarm time + #[serde(default)] + pub auto_wake_time: Option>, + + /// Alarm weekday repeat + #[serde(default)] + pub auto_wake_repeat: u8, + + /// Single tap enable + #[serde(default)] + pub single_tap_enable: bool, + + /// Single tap shell script + #[serde(default)] + pub single_tap_shell: String, + + /// Double tap enable + #[serde(default)] + pub double_tap_enable: bool, + + /// Double tap shell script + #[serde(default)] + pub double_tap_shell: String, + + /// Long tap enable + #[serde(default)] + pub long_tap_enable: bool, + + /// Long tap shell script + #[serde(default)] + pub long_tap_shell: String, + + /// Auto shutdown when battery level is low + #[serde(default)] + pub auto_shutdown_level: Option, + + /// Auto shutdown delay, seconds + #[serde(default)] + pub auto_shutdown_delay: Option, + + /// Charging range + #[serde(default)] + pub auto_charging_range: Option<(f32, f32)>, + + /// Keep charging duration + #[serde(default)] + pub full_charge_duration: Option, + + /// UPS automatically power on when power recovered + #[serde(default)] + pub auto_power_on: Option, + + /// Soft poweroff, PiSugar 3 only + #[serde(default)] + pub soft_poweroff: Option, + + /// Soft poweroff shell script + #[serde(default)] + pub soft_poweroff_shell: Option, + + /// Auto rtc sync + #[serde(default)] + pub auto_rtc_sync: Option, + + /// RTC ppm adjust comm (every second) + #[serde(default)] + pub adj_comm: Option, + + /// RTC ppm adjust diff (in 31s) + #[serde(default)] + pub adj_diff: Option, + + /// RTC adjust ppm + #[serde(default)] + pub rtc_adj_ppm: Option, + + /// Anti mistouch + #[serde(default)] + pub anti_mistouch: Option, + + /// Battery hardware protect + #[serde(default)] + pub bat_protect: Option, + + /// User defined battery curve + #[serde(default)] + pub battery_curve: Option>, +} + +impl PiSugarConfig { + fn _validate_battery_curve(cfg: &PiSugarConfig) -> bool { + let mut curve = cfg.battery_curve.clone().unwrap_or_default(); + curve.sort_by(|x, y| x.0.total_cmp(&y.0)); + for i in 1..curve.len() { + if curve[i].0 == curve[i - 1].0 || curve[i].1 <= curve[i - 1].1 { + log::error!("Invalid customized battery curve {:?} {:?}", curve[i - 1], curve[i]); + return false; + } + } + true + } + + pub fn load(&mut self, path: &Path) -> io::Result<()> { + let mut f = File::open(path)?; + let mut buff = String::new(); + let _ = f.read_to_string(&mut buff)?; + let config = serde_json::from_str(&buff)?; + if !PiSugarConfig::_validate_battery_curve(&config) { + return Err(io::ErrorKind::InvalidData.into()); + } + *self = config; + Ok(()) + } + + pub fn save_to(&self, path: &Path) -> io::Result<()> { + let mut options = OpenOptions::new(); + options.write(true).create(true); + let mut f = options.open(path)?; + let s = serde_json::to_string_pretty(self)?; + log::info!("Dump config:\n{}", s); + f.set_len(0)?; + f.write_all(s.as_bytes()) + } +} + +impl Default for PiSugarConfig { + fn default() -> Self { + Self { + auth_user: Default::default(), + auth_password: Default::default(), + session_timeout: default_session_timeout(), + i2c_bus: default_i2c_bus(), + i2c_addr: Default::default(), + auto_wake_time: Default::default(), + auto_wake_repeat: Default::default(), + single_tap_enable: Default::default(), + single_tap_shell: Default::default(), + double_tap_enable: Default::default(), + double_tap_shell: Default::default(), + long_tap_enable: Default::default(), + long_tap_shell: Default::default(), + auto_shutdown_level: Default::default(), + auto_shutdown_delay: Default::default(), + auto_charging_range: Default::default(), + full_charge_duration: Default::default(), + auto_power_on: Default::default(), + soft_poweroff: Default::default(), + soft_poweroff_shell: Default::default(), + auto_rtc_sync: Default::default(), + adj_comm: Default::default(), + adj_diff: Default::default(), + rtc_adj_ppm: Default::default(), + anti_mistouch: Default::default(), + bat_protect: Default::default(), + battery_curve: Default::default(), + } + } +} diff --git a/pisugar-core/src/ip5209.rs b/pisugar-core/src/ip5209.rs index 99e6184..d195c12 100644 --- a/pisugar-core/src/ip5209.rs +++ b/pisugar-core/src/ip5209.rs @@ -3,8 +3,12 @@ use std::time::Instant; use rppal::i2c::I2c; -use crate::battery::{Battery, BatteryEvent}; -use crate::{convert_battery_voltage_to_level, gpio_detect_tap, BatteryThreshold, Error, Model, PiSugarConfig, Result}; +use crate::config::BatteryThreshold; +use crate::{ + battery::{Battery, BatteryEvent}, + I2C_ADDR_BAT, +}; +use crate::{convert_battery_voltage_to_level, gpio_detect_tap, Error, Model, PiSugarConfig, Result}; /// Battery threshold curve pub const BATTERY_CURVE: [BatteryThreshold; 10] = [ @@ -54,9 +58,9 @@ impl IP5209 { } /// Parse level(%) - pub fn parse_voltage_level(voltage: f32) -> f32 { + pub fn parse_voltage_level(voltage: f32, curve: &[BatteryThreshold]) -> f32 { if voltage > 0.0 { - convert_battery_voltage_to_level(voltage, &BATTERY_CURVE) + convert_battery_voltage_to_level(voltage, curve) } else { 100.0 } @@ -245,11 +249,12 @@ pub struct IP5209Battery { levels: VecDeque, intensities: VecDeque<(Instant, f32)>, tap_history: String, + cfg: PiSugarConfig, } impl IP5209Battery { - pub fn new(i2c_bus: u8, i2c_addr: u16, model: Model) -> Result { - let ip5209 = IP5209::new(i2c_bus, i2c_addr)?; + pub fn new(cfg: PiSugarConfig, model: Model) -> Result { + let ip5209 = IP5209::new(cfg.i2c_bus, cfg.i2c_addr.unwrap_or(I2C_ADDR_BAT))?; Ok(Self { ip5209, model, @@ -257,6 +262,7 @@ impl IP5209Battery { intensities: VecDeque::with_capacity(30), levels: VecDeque::with_capacity(30), tap_history: String::with_capacity(30), + cfg, }) } } @@ -317,7 +323,13 @@ impl Battery for IP5209Battery { } fn level(&self) -> Result { - self.voltage_avg().map(IP5209::parse_voltage_level) + let curve = self + .cfg + .battery_curve + .as_ref() + .map(|x| &x[..]) + .unwrap_or(BATTERY_CURVE.as_ref()); + self.voltage_avg().map(|x| IP5209::parse_voltage_level(x, curve)) } fn intensity(&self) -> Result { @@ -397,11 +409,11 @@ impl Battery for IP5209Battery { } self.voltages.push_back((now, voltage)); - let level = IP5209::parse_voltage_level(voltage); + let level = self.level()?; if self.levels.len() >= self.levels.capacity() { self.levels.pop_front(); } - self.levels.push_back(level as f32); + self.levels.push_back(level); let intensity = self.intensity()?; if self.intensities.len() >= self.intensities.capacity() { diff --git a/pisugar-core/src/ip5312.rs b/pisugar-core/src/ip5312.rs index 511e7f7..07a70f5 100644 --- a/pisugar-core/src/ip5312.rs +++ b/pisugar-core/src/ip5312.rs @@ -3,10 +3,13 @@ use std::time::Instant; use rppal::i2c::I2c; -use crate::battery::{Battery, BatteryEvent}; +use crate::Error; +use crate::{ + battery::{Battery, BatteryEvent}, + config::BatteryThreshold, +}; use crate::{convert_battery_voltage_to_level, I2cError, Model, PiSugarConfig}; use crate::{gpio_detect_tap, Result}; -use crate::{BatteryThreshold, Error}; /// Battery threshold curve pub const BATTERY_CURVE: [BatteryThreshold; 10] = [ @@ -53,9 +56,9 @@ impl IP5312 { } /// Parse level(%) - pub fn parse_voltage_level(voltage: f32) -> f32 { + pub fn parse_voltage_level(voltage: f32, curve: &[BatteryThreshold]) -> f32 { if voltage > 0.0 { - convert_battery_voltage_to_level(voltage, &BATTERY_CURVE) + convert_battery_voltage_to_level(voltage, curve) } else { 100.0 } @@ -242,11 +245,12 @@ pub struct IP5312Battery { intensities: VecDeque<(Instant, f32)>, levels: VecDeque, tap_history: String, + cfg: PiSugarConfig, } impl IP5312Battery { - pub fn new(i2c_bus: u8, i2c_addr: u16, model: Model) -> Result { - let ip5312 = IP5312::new(i2c_bus, i2c_addr)?; + pub fn new(cfg: PiSugarConfig, model: Model) -> Result { + let ip5312 = IP5312::new(cfg.i2c_bus, cfg.i2c_addr.unwrap_or(model.default_battery_i2c_addr()))?; Ok(Self { ip5312, model, @@ -254,6 +258,7 @@ impl IP5312Battery { intensities: VecDeque::with_capacity(30), levels: VecDeque::with_capacity(30), tap_history: String::with_capacity(30), + cfg, }) } } @@ -315,7 +320,13 @@ impl Battery for IP5312Battery { } fn level(&self) -> Result { - self.voltage_avg().map(IP5312::parse_voltage_level) + let curve = self + .cfg + .battery_curve + .as_ref() + .map(|x| &x[..]) + .unwrap_or(BATTERY_CURVE.as_ref()); + self.voltage_avg().map(|x| IP5312::parse_voltage_level(x, curve)) } fn intensity(&self) -> Result { @@ -395,7 +406,7 @@ impl Battery for IP5312Battery { self.voltages.push_back((now, voltage)); } - let level = IP5312::parse_voltage_level(voltage); + let level = self.level()?; self.levels.pop_front(); while self.levels.len() < self.levels.capacity() { self.levels.push_back(level); diff --git a/pisugar-core/src/lib.rs b/pisugar-core/src/lib.rs index 684e51a..e775ff6 100644 --- a/pisugar-core/src/lib.rs +++ b/pisugar-core/src/lib.rs @@ -1,9 +1,9 @@ use std::convert::{From, TryInto}; use std::fmt; use std::fmt::{Display, Formatter}; -use std::fs::{File, OpenOptions}; + use std::io; -use std::io::{Read, Write}; +use std::io::Write; use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus}; use std::thread; @@ -11,9 +11,9 @@ use std::time::{Duration, Instant}; use battery::BatteryEvent; use chrono::{DateTime, Datelike, Local, Timelike}; +pub use config::{BatteryThreshold, PiSugarConfig}; use hyper::client::Client; use rppal::i2c::Error as I2cError; -use serde::{Deserialize, Serialize}; pub use model::Model; pub use sd3078::*; @@ -23,6 +23,7 @@ pub use crate::rtc::RTCRawTime; use crate::rtc::RTC; mod battery; +mod config; mod ip5209; mod ip5312; mod model; @@ -86,9 +87,6 @@ impl Display for Error { /// PiSugar result pub type Result = std::result::Result; -/// Battery voltage threshold, (low, percentage at low) -type BatteryThreshold = (f32, f32); - /// Battery voltage to percentage level fn convert_battery_voltage_to_level(voltage: f32, battery_curve: &[BatteryThreshold]) -> f32 { for i in 0..battery_curve.len() { @@ -131,176 +129,6 @@ pub fn sys_write_time(dt: DateTime) { } } -fn default_i2c_bus() -> u8 { - 1 -} - -/// Default auth session timeout, 1h -fn default_session_timeout() -> u32 { - 60 * 60 -} - -/// PiSugar configuration -#[derive(Clone, Serialize, Deserialize)] -pub struct PiSugarConfig { - /// Http digest auth - #[serde(default)] - pub auth_user: Option, - - #[serde(default)] - pub auth_password: Option, - - /// Auth session timeout in seconds - #[serde(default = "default_session_timeout")] - pub session_timeout: u32, - - /// I2C bus, default 1 (/dev/i2c-1) - #[serde(default = "default_i2c_bus")] - pub i2c_bus: u8, - - /// I2C addr, default 0x57 (87), available in PiSugar3 - #[serde(default)] - pub i2c_addr: Option, - - /// Alarm time - #[serde(default)] - pub auto_wake_time: Option>, - - /// Alarm weekday repeat - #[serde(default)] - pub auto_wake_repeat: u8, - - /// Single tap enable - #[serde(default)] - pub single_tap_enable: bool, - - /// Single tap shell script - #[serde(default)] - pub single_tap_shell: String, - - /// Double tap enable - #[serde(default)] - pub double_tap_enable: bool, - - /// Double tap shell script - #[serde(default)] - pub double_tap_shell: String, - - /// Long tap enable - #[serde(default)] - pub long_tap_enable: bool, - - /// Long tap shell script - #[serde(default)] - pub long_tap_shell: String, - - /// Auto shutdown when battery level is low - #[serde(default)] - pub auto_shutdown_level: Option, - - /// Auto shutdown delay, seconds - #[serde(default)] - pub auto_shutdown_delay: Option, - - /// Charging range - #[serde(default)] - pub auto_charging_range: Option<(f32, f32)>, - - /// Keep charging duration - #[serde(default)] - pub full_charge_duration: Option, - - /// UPS automatically power on when power recovered - #[serde(default)] - pub auto_power_on: Option, - - /// Soft poweroff, PiSugar 3 only - #[serde(default)] - pub soft_poweroff: Option, - - /// Soft poweroff shell script - #[serde(default)] - pub soft_poweroff_shell: Option, - - /// Auto rtc sync - #[serde(default)] - pub auto_rtc_sync: Option, - - /// RTC ppm adjust comm (every second) - #[serde(default)] - pub adj_comm: Option, - - /// RTC ppm adjust diff (in 31s) - #[serde(default)] - pub adj_diff: Option, - - /// RTC adjust ppm - #[serde(default)] - pub rtc_adj_ppm: Option, - - /// Anti mistouch - #[serde(default)] - pub anti_mistouch: Option, - - /// Battery hardware protect - #[serde(default)] - pub bat_protect: Option, -} - -impl PiSugarConfig { - pub fn load(&mut self, path: &Path) -> io::Result<()> { - let mut f = File::open(path)?; - let mut buff = String::new(); - let _ = f.read_to_string(&mut buff)?; - let config = serde_json::from_str(&buff)?; - *self = config; - Ok(()) - } - - pub fn save_to(&self, path: &Path) -> io::Result<()> { - let mut options = OpenOptions::new(); - options.write(true).create(true); - let mut f = options.open(path)?; - let s = serde_json::to_string_pretty(self)?; - log::info!("Dump config:\n{}", s); - f.set_len(0)?; - f.write_all(s.as_bytes()) - } -} - -impl Default for PiSugarConfig { - fn default() -> Self { - Self { - auth_user: Default::default(), - auth_password: Default::default(), - session_timeout: default_session_timeout(), - i2c_bus: default_i2c_bus(), - i2c_addr: Default::default(), - auto_wake_time: Default::default(), - auto_wake_repeat: Default::default(), - single_tap_enable: Default::default(), - single_tap_shell: Default::default(), - double_tap_enable: Default::default(), - double_tap_shell: Default::default(), - long_tap_enable: Default::default(), - long_tap_shell: Default::default(), - auto_shutdown_level: Default::default(), - auto_shutdown_delay: Default::default(), - auto_charging_range: Default::default(), - full_charge_duration: Default::default(), - auto_power_on: Default::default(), - soft_poweroff: Default::default(), - soft_poweroff_shell: Default::default(), - auto_rtc_sync: Default::default(), - adj_comm: Default::default(), - adj_diff: Default::default(), - rtc_adj_ppm: Default::default(), - anti_mistouch: Default::default(), - bat_protect: Default::default(), - } - } -} - /// Button tap type #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum TapType { @@ -349,7 +177,7 @@ pub fn gpio_detect_tap(gpio_history: &mut String) -> Option { /// Execute shell with sh pub fn execute_shell(shell: &str) -> io::Result { let args = ["-c", shell]; - let mut child = Command::new("/bin/sh").args(&args).spawn()?; + let mut child = Command::new("/bin/sh").args(args).spawn()?; child.wait() } @@ -416,7 +244,7 @@ impl PiSugarCore { fn init_battery(&mut self) -> Result<()> { if self.battery.is_none() { log::debug!("Core init battery..."); - let mut battery = self.model.bind(self.config.i2c_bus, self.config.i2c_addr)?; + let mut battery = self.model.bind(self.config.clone())?; battery.init(&self.config)?; self.battery = Some(battery); } @@ -426,7 +254,7 @@ impl PiSugarCore { fn init_rtc(&mut self) -> Result<()> { if self.rtc.is_none() { log::debug!("Core init rtc..."); - let mut rtc = self.model.rtc(self.config.i2c_bus, self.config.i2c_addr)?; + let mut rtc = self.model.rtc(self.config.clone())?; rtc.init(&self.config)?; self.rtc = Some(rtc); } @@ -464,8 +292,8 @@ impl PiSugarCore { poll_check_at: Instant::now(), rtc_sync_at: Instant::now(), }; - core.battery = Some(model.bind(config.i2c_bus, config.i2c_addr)?); - core.rtc = Some(model.rtc(config.i2c_bus, config.i2c_addr)?); + core.battery = Some(model.bind(config.clone())?); + core.rtc = Some(model.rtc(config.clone())?); Ok(core) } diff --git a/pisugar-core/src/model.rs b/pisugar-core/src/model.rs index 38eefbb..331fcfa 100644 --- a/pisugar-core/src/model.rs +++ b/pisugar-core/src/model.rs @@ -1,11 +1,11 @@ use std::convert::TryFrom; use std::fmt; -use crate::ip5209::IP5209Battery; use crate::ip5312::IP5312Battery; use crate::pisugar3::{PiSugar3Battery, PiSugar3RTC, I2C_ADDR_P3}; use crate::rtc::RTC; use crate::{battery::Battery, I2C_ADDR_BAT}; +use crate::{config::PiSugarConfig, ip5209::IP5209Battery}; use crate::{Result, I2C_ADDR_RTC, SD3078}; const PISUGAR_2_4LEDS: &str = "PiSugar 2 (4-LEDs)"; @@ -47,30 +47,30 @@ impl Model { } } - pub fn bind(&self, i2c_bus: u8, i2c_addr: Option) -> Result> { - let i2c_addr = if *self == Model::PiSugar_3 { - i2c_addr.unwrap_or_else(|| self.default_battery_i2c_addr()) - } else { - self.default_battery_i2c_addr() - }; + pub fn bind(&self, cfg: PiSugarConfig) -> Result> { + log::info!( + "Binding battery i2c bus={} addr={}", + cfg.i2c_bus, + cfg.i2c_addr.unwrap_or(self.default_battery_i2c_addr()) + ); let b: Box = match *self { - Model::PiSugar_2_4LEDs => Box::new(IP5209Battery::new(i2c_bus, i2c_addr, *self)?), - Model::PiSugar_2_2LEDs => Box::new(IP5209Battery::new(i2c_bus, i2c_addr, *self)?), - Model::PiSugar_2_Pro => Box::new(IP5312Battery::new(i2c_bus, i2c_addr, *self)?), - Model::PiSugar_3 => Box::new(PiSugar3Battery::new(i2c_bus, i2c_addr, *self)?), + Model::PiSugar_2_4LEDs => Box::new(IP5209Battery::new(cfg, *self)?), + Model::PiSugar_2_2LEDs => Box::new(IP5209Battery::new(cfg, *self)?), + Model::PiSugar_2_Pro => Box::new(IP5312Battery::new(cfg, *self)?), + Model::PiSugar_3 => Box::new(PiSugar3Battery::new(cfg, *self)?), }; Ok(b) } - pub fn rtc(&self, i2c_bus: u8, i2c_addr: Option) -> Result> { - let i2c_addr = if *self == Model::PiSugar_3 { - i2c_addr.unwrap_or_else(|| self.default_rtc_i2c_addr()) - } else { + pub fn rtc(&self, cfg: PiSugarConfig) -> Result> { + log::info!( + "Bindig rtc i2c bus={} addr={}", + cfg.i2c_bus, self.default_rtc_i2c_addr() - }; + ); let r: Box = match *self { - Model::PiSugar_3 => Box::new(PiSugar3RTC::new(i2c_bus, i2c_addr)?), - _ => Box::new(SD3078::new(i2c_bus, i2c_addr)?), + Model::PiSugar_3 => Box::new(PiSugar3RTC::new(cfg, *self)?), + _ => Box::new(SD3078::new(cfg, *self)?), }; Ok(r) } diff --git a/pisugar-core/src/pisugar3.rs b/pisugar-core/src/pisugar3.rs index 99faa06..150866f 100644 --- a/pisugar-core/src/pisugar3.rs +++ b/pisugar-core/src/pisugar3.rs @@ -6,9 +6,12 @@ use std::time::Instant; use rppal::i2c::I2c; -use crate::battery::{Battery, BatteryEvent}; use crate::ip5312::IP5312; use crate::rtc::{bcd_to_dec, dec_to_bcd, RTC}; +use crate::{ + battery::{Battery, BatteryEvent}, + ip5312::BATTERY_CURVE, +}; use crate::{Error, Model, PiSugarConfig, RTCRawTime, Result, TapType}; /// PiSugar 3 i2c addr @@ -409,12 +412,12 @@ pub struct PiSugar3Battery { levels: VecDeque, poll_at: Instant, version: String, + cfg: PiSugarConfig, } impl PiSugar3Battery { - pub fn new(i2c_bus: u8, i2c_addr: u16, model: Model) -> Result { - log::info!("PiSugar3 battery bus: 0x{:02x} addr: 0x{:02x}", i2c_bus, i2c_addr); - let pisugar3 = PiSugar3::new(i2c_bus, i2c_addr)?; + pub fn new(cfg: PiSugarConfig, model: Model) -> Result { + let pisugar3 = PiSugar3::new(cfg.i2c_bus, cfg.i2c_addr.unwrap_or(model.default_battery_i2c_addr()))?; let poll_at = Instant::now() - std::time::Duration::from_secs(10); Ok(Self { pisugar3, @@ -424,6 +427,7 @@ impl PiSugar3Battery { levels: VecDeque::with_capacity(30), poll_at, version: "".to_string(), + cfg, }) } } @@ -479,7 +483,13 @@ impl Battery for PiSugar3Battery { } fn level(&self) -> crate::Result { - self.voltage_avg().map(|v| IP5312::parse_voltage_level(v)) + let curve = self + .cfg + .battery_curve + .as_ref() + .map(|x| &x[..]) + .unwrap_or(BATTERY_CURVE.as_ref()); + self.voltage_avg().map(|v| IP5312::parse_voltage_level(v, curve)) } fn intensity(&self) -> crate::Result { @@ -555,7 +565,7 @@ impl Battery for PiSugar3Battery { self.voltages.push_back((now, voltage)); } - let level = IP5312::parse_voltage_level(voltage); + let level = self.level()?; self.levels.pop_front(); while self.levels.len() < self.levels.capacity() { self.levels.push_back(level); @@ -633,13 +643,13 @@ impl Battery for PiSugar3Battery { pub struct PiSugar3RTC { pisugar3: PiSugar3, + cfg: PiSugarConfig, } impl PiSugar3RTC { - pub fn new(i2c_bus: u8, i2c_addr: u16) -> Result { - log::debug!("PiSugar3 rtc bus: 0x{:02x} addr: 0x{:02x}", i2c_bus, i2c_addr); - let pisugar3 = PiSugar3::new(i2c_bus, i2c_addr)?; - Ok(Self { pisugar3 }) + pub fn new(cfg: PiSugarConfig, model: Model) -> Result { + let pisugar3 = PiSugar3::new(cfg.i2c_bus, model.default_rtc_i2c_addr())?; + Ok(Self { pisugar3, cfg }) } } diff --git a/pisugar-core/src/sd3078.rs b/pisugar-core/src/sd3078.rs index fc84c40..3d67201 100644 --- a/pisugar-core/src/sd3078.rs +++ b/pisugar-core/src/sd3078.rs @@ -1,19 +1,23 @@ use rppal::i2c::I2c; -use crate::rtc::{bcd_to_dec, dec_to_bcd, RTCRawTime, RTC}; +use crate::{ + rtc::{bcd_to_dec, dec_to_bcd, RTCRawTime, RTC}, + Model, +}; use crate::{PiSugarConfig, Result}; /// SD3078, rtc chip pub struct SD3078 { i2c: I2c, + cfg: PiSugarConfig, } impl SD3078 { /// Create new SD3078 - pub fn new(i2c_bus: u8, i2c_addr: u16) -> Result { - let mut i2c = I2c::with_bus(i2c_bus)?; - i2c.set_slave_address(i2c_addr)?; - Ok(Self { i2c }) + pub fn new(cfg: PiSugarConfig, model: Model) -> Result { + let mut i2c = I2c::with_bus(cfg.i2c_bus)?; + i2c.set_slave_address(model.default_rtc_i2c_addr())?; + Ok(Self { i2c, cfg }) } /// Disable write protect