fix: use same curves as pisugar-power-manager

This commit is contained in:
Jdaie
2025-03-03 08:49:02 +08:00
parent 6bab0b36d1
commit 8da625e7e2

View File

@ -17,26 +17,9 @@ PiSugar_addresses = {
"PiSugar3": 0x57, # PiSugar3\3Plus "PiSugar3": 0x57, # PiSugar3\3Plus
"PiSugar2 RTC": 0x32 # PiSugar2\2Plus RTC "PiSugar2 RTC": 0x32 # PiSugar2\2Plus RTC
} }
curve1200 = [
(4.16, 100.0), # Use the same battery level curve as pisugar-power-manager
(4.05, 95.0), curve5312 = [
(4.00, 80.0),
(3.92, 65.0),
(3.86, 40.0),
(3.79, 25.5),
(3.66, 10.0),
(3.52, 6.5),
(3.49, 3.2),
(3.1, 0.0),
]
curve1200_3 = [
(4.2, 100.0), # 高电量阶段 (100%)
(4.0, 80.0), # 中电量阶段 (80%)
(3.7, 60.0), # 中电量阶段 (60%)
(3.5, 20.0), # 低电量阶段 (20%)
(3.1, 0.0) # 电量耗尽 (0%)
]
curve5000 = [
(4.10, 100.0), (4.10, 100.0),
(4.05, 95.0), (4.05, 95.0),
(3.90, 88.0), (3.90, 88.0),
@ -49,8 +32,18 @@ curve5000 = [
(3.1, 0.0), (3.1, 0.0),
] ]
curve5209 = [
(4.16, 100.0),
(4.05, 95.0),
(4.00, 80.0),
(3.92, 65.0),
(3.86, 40.0),
(3.79, 25.5),
(3.66, 10.0),
(3.52, 6.5),
(3.49, 3.2),
(3.1, 0.0),
]
class PiSugarServer: class PiSugarServer:
def __init__(self): def __init__(self):
@ -59,7 +52,7 @@ class PiSugarServer:
""" """
self._bus = smbus.SMBus(1) self._bus = smbus.SMBus(1)
self.ready = False self.ready = False
self.modle = None self.model = None
self.i2creg = [] self.i2creg = []
self.address = 0 self.address = 0
self.battery_voltage = 0.00 self.battery_voltage = 0.00
@ -82,83 +75,82 @@ class PiSugarServer:
""" """
Attempt to connect to the PiSugar device in a background thread. Attempt to connect to the PiSugar device in a background thread.
""" """
while self.modle is None: while self.model is None:
if self.check_device(PiSugar_addresses["PiSugar2"]) is not None: if self.check_device(PiSugar_addresses["PiSugar2"]) is not None:
self.address = PiSugar_addresses["PiSugar2"] self.address = PiSugar_addresses["PiSugar2"]
if self.check_device(PiSugar_addresses["PiSugar2"], 0xC2) != 0: if self.check_device(PiSugar_addresses["PiSugar2"], 0xC2) != 0:
self.modle = "PiSugar2Plus" self.model = "PiSugar2Plus"
else: else:
self.modle = "PiSugar2" self.model = "PiSugar2"
self.device_init() self.device_init()
elif self.check_device(PiSugar_addresses["PiSugar3"]) is not None: elif self.check_device(PiSugar_addresses["PiSugar3"]) is not None:
self.modle = 'PiSugar3' self.model = 'PiSugar3'
self.address = PiSugar_addresses["PiSugar3"] self.address = PiSugar_addresses["PiSugar3"]
self.device_init() self.device_init()
else: else:
self.modle = None self.model = None
logging.info( logging.info(
"No PiSugar device was found. Please check if the PiSugar device is powered on." "No PiSugar device was found. Please check if the PiSugar device is powered on."
) )
time.sleep(5) time.sleep(5)
logging.info(f"{self.modle} is connected") logging.info(f"{self.model} is connected")
# Once connected, start the timer # Once connected, start the timer
self.start_timer() self.start_timer()
while len(self.i2creg) < 256: while len(self.i2creg) < 256:
time.sleep(1) time.sleep(1)
self.ready = True self.ready = True
logging.info(f"{self.modle} is ready") logging.info(f"{self.model} is ready")
def start_timer(self): def start_timer(self):
# Create a thread to execute the timer function
# 创建一个线程来执行定时函数
timer_thread = threading.Thread(target=self.update_value) timer_thread = threading.Thread(target=self.update_value)
timer_thread.daemon = True # 设置为守护线程,主程序退出时自动结束 timer_thread.daemon = True # Set as a daemon thread, automatically ends when the main program exits
timer_thread.start() timer_thread.start()
def update_value(self): def update_value(self):
"""每三秒更新pisugar状态包括触发自动关机""" """Update PiSugar status every three seconds, including triggering auto shutdown"""
while True: while True:
try: try:
if( self.modle == 'PiSugar2') | (self.modle == 'PiSugar2Plus'): if self.model == 'PiSugar2' or self.model == 'PiSugar2Plus':
self.set_battery_notallow_charging() #短暂关闭充电以获取准确电池电压 self.set_battery_notallow_charging() # Temporarily disable charging to get accurate battery voltage
time.sleep(0.05) time.sleep(0.05)
self.i2creg = [] self.i2creg = []
for i in range(0, 256, 32): for i in range(0, 256, 32):
# 计算当前读取的起始寄存器地址 # Calculate the starting register address for the current read
current_register = 0 + i current_register = 0 + i
# 计算当前读取的数据长度 # Calculate the length of the current read
current_length = min(32, 256 - i) current_length = min(32, 256 - i)
# 读取数据块 # Read data block
chunk = self._bus.read_i2c_block_data( chunk = self._bus.read_i2c_block_data(
self.address, current_register, current_length) self.address, current_register, current_length)
# 将读取的数据块添加到结果列表中 # Add the read data block to the result list
self.i2creg.extend(chunk) self.i2creg.extend(chunk)
time.sleep(0.1) time.sleep(0.1)
logging.debug(f"Data length: {len(self.i2creg)}") logging.debug(f"Data length: {len(self.i2creg)}")
logging.debug(f"Data: {self.i2creg}") logging.debug(f"Data: {self.i2creg}")
if self.modle == 'PiSugar3': if self.model == 'PiSugar3':
low = self.i2creg[0x23] low = self.i2creg[0x23]
high = self.i2creg[0x22] high = self.i2creg[0x22]
self.battery_voltage = (((high << 8) + low) / 1000) self.battery_voltage = (((high << 8) + low) / 1000)
self.temperature = self.i2creg[0x04]-40 self.temperature = self.i2creg[0x04] - 40
ctr1 = self.i2creg[0x02] # 读取控制寄存器 1 ctr1 = self.i2creg[0x02] # Read control register 1
self.power_plugged = (ctr1 & (1 << 7)) != 0 # 检查电源是否插入 self.power_plugged = (ctr1 & (1 << 7)) != 0 # Check if power is plugged in
self.allow_charging = (ctr1 & (1 << 6)) != 0 # 检查是否允许充电 self.allow_charging = (ctr1 & (1 << 6)) != 0 # Check if charging is allowed
if self.max_charge_voltage_protection: if self.max_charge_voltage_protection:
self._bus.write_byte_data( self._bus.write_byte_data(
self.address, 0x0B, 0x29) # 关闭写保护 self.address, 0x0B, 0x29) # Disable write protection
self._bus.write_byte_data(self.address, 0x20, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x20, self._bus.read_byte_data(
self.address, 0x20) | 0b10000000) self.address, 0x20) | 0b10000000)
self._bus.write_byte_data( self._bus.write_byte_data(
self.address, 0x0B, 0x00) # 开启写保护 self.address, 0x0B, 0x00) # Enable write protection
else: else:
self._bus.write_byte_data( self._bus.write_byte_data(
self.address, 0x0B, 0x29) # 关闭写保护 self.address, 0x0B, 0x29) # Disable write protection
self._bus.write_byte_data(self.address, 0x20, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x20, self._bus.read_byte_data(
self.address, 0x20) & 0b01111111) self.address, 0x20) & 0b01111111)
self._bus.write_byte_data( self._bus.write_byte_data(
self.address, 0x0B, 0x00) # 开启写保护 self.address, 0x0B, 0x00) # Enable write protection
elif self.modle == 'PiSugar2': elif self.model == 'PiSugar2':
high = self.i2creg[0xa3] high = self.i2creg[0xa3]
low = self.i2creg[0xa2] low = self.i2creg[0xa2]
self.battery_voltage = (2600.0 - (((high | 0b11000000) << 8) + low) * 0.26855) / \ self.battery_voltage = (2600.0 - (((high | 0b11000000) << 8) + low) * 0.26855) / \
@ -176,7 +168,7 @@ class PiSugarServer:
else: else:
self.set_battery_allow_charging() self.set_battery_allow_charging()
elif self.modle == 'PiSugar2Plus': elif self.model == 'PiSugar2Plus':
low = self.i2creg[0xd0] low = self.i2creg[0xd0]
high = self.i2creg[0xd1] high = self.i2creg[0xd1]
self.battery_voltage = ( self.battery_voltage = (
@ -207,17 +199,17 @@ class PiSugarServer:
def shutdown(self): def shutdown(self):
# logging.info("[PiSugarX] PiSugar set shutdown .") # logging.info("[PiSugarX] PiSugar set shutdown .")
if self.modle == 'PiSugar3': if self.model == 'PiSugar3':
# 10秒后关闭电源 # Shutdown the power after 10 seconds
self._bus.write_byte_data(self.address, 0x0B, 0x29) # 关闭写保护 self._bus.write_byte_data(self.address, 0x0B, 0x29) # Disable write protection
self._bus.write_byte_data(self.address, 0x09, 10) self._bus.write_byte_data(self.address, 0x09, 10)
self._bus.write_byte_data(self.address, 0x02, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x02, self._bus.read_byte_data(
self.address, 0x02) & 0b11011111) self.address, 0x02) & 0b11011111)
self._bus.write_byte_data(self.address, 0x0B, 0x00) # 开启写保护 self._bus.write_byte_data(self.address, 0x0B, 0x00) # Enable write protection
logging.info("[PiSugarX] PiSugar shutdown in 10s.") logging.info("[PiSugarX] PiSugar shutdown in 10s.")
elif self.modle == 'PiSugar2': elif self.model == 'PiSugar2':
pass pass
elif self.modle == 'PiSugar2Plus': elif self.model == 'PiSugar2Plus':
pass pass
def check_device(self, address, reg=0): def check_device(self, address, reg=0):
@ -230,8 +222,8 @@ class PiSugarServer:
def device_init(self): def device_init(self):
if self.modle == "PiSugar2Plus": if self.model == "PiSugar2Plus":
'''初始化GPIO''' '''Initialize GPIO'''
self._bus.write_byte_data(self.address, 0x52, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x52, self._bus.read_byte_data(
self.address, 0x52) | 0b00000010) self.address, 0x52) | 0b00000010)
self._bus.write_byte_data(self.address, 0x54, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x54, self._bus.read_byte_data(
@ -244,14 +236,14 @@ class PiSugarServer:
self.address, 0x52) & 0b10011111 | 0b01000000) self.address, 0x52) & 0b10011111 | 0b01000000)
self._bus.write_byte_data(self.address, 0xc2, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0xc2, self._bus.read_byte_data(
self.address, 0xc2) | 0b00010000) self.address, 0xc2) | 0b00010000)
logging.debug(f"PiSugar2Plus GPIO 初始化完毕") logging.debug(f"PiSugar2Plus GPIO initialization complete")
'''Init boost intensity, 0x3f*50ma, 3A''' '''Init boost intensity, 0x3f*50ma, 3A'''
self._bus.write_byte_data(self.address, 0x30, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x30, self._bus.read_byte_data(
self.address, 0x30) & 0b11000000 | 0x3f) self.address, 0x30) & 0b11000000 | 0x3f)
logging.debug(f"PiSugar2Plus 电流设置完毕") logging.debug(f"PiSugar2Plus current setting complete")
elif self.modle == "PiSugar2": elif self.model == "PiSugar2":
'''初始化GPIO''' '''Initialize GPIO'''
self._bus.write_byte_data(self.address, 0x51, (self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x51, (self._bus.read_byte_data(
self.address, 0x51) & 0b11110011) | 0b00000100) self.address, 0x51) & 0b11110011) | 0b00000100)
self._bus.write_byte_data(self.address, 0x53, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x53, self._bus.read_byte_data(
@ -264,41 +256,43 @@ class PiSugarServer:
self.address, 0x52) & 0b11110011) | 0b00000100) self.address, 0x52) & 0b11110011) | 0b00000100)
self._bus.write_byte_data(self.address, 0x53, (self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x53, (self._bus.read_byte_data(
self.address, 0x53) & 0b11101111) | 0b00010000) self.address, 0x53) & 0b11101111) | 0b00010000)
logging.debug(f"PiSugar2 GPIO 初始化完毕") logging.debug(f"PiSugar2 GPIO initialization complete")
pass pass
def convert_battery_voltage_to_level(self): def convert_battery_voltage_to_level(self):
""" """
将电池电压转换为电量百分比。 Convert battery voltage to battery percentage.
:param voltage: 当前电池电压 :param voltage: Current battery voltage
:param curve: 电池阈值曲线,格式为 [(电压1, 电量1), (电压2, 电量2), ...] :param curve: Battery threshold curve, format [(voltage1, percentage1), (voltage2, percentage2), ...]
:return: 电量百分比 :return: Battery percentage
""" """
if (self.modle == "PiSugar2Plus") | (self.modle == "PiSugar3Plus"): if self.model == "PiSugar2Plus":
curve = curve5000 curve = curve5312
elif self.modle == "PiSugar2": elif self.model == "PiSugar3Plus":
curve = curve1200 curve = curve5312
elif self.modle == "PiSugar3": elif self.model == "PiSugar2":
curve = curve1200_3 curve = curve5209
# 将当前电压加入历史记录 elif self.model == "PiSugar3":
curve = curve5312
# Add the current voltage to the history
# 如果历史记录不足 5 次,直接返回平均值(避免截尾后无有效数据) # If the history is less than 5 entries, return the average directly (to avoid truncation with no valid data)
if len(self.voltage_history) < 5: if len(self.voltage_history) < 5:
avg_voltage = sum(self.voltage_history) / len(self.voltage_history) avg_voltage = sum(self.voltage_history) / len(self.voltage_history)
else: else:
# 排序后去掉最高 2 个和最低 2 个 # Sort and remove the highest 2 and lowest 2
sorted_history = sorted(self.voltage_history) sorted_history = sorted(self.voltage_history)
trimmed_history = sorted_history[2:-2] # 去掉前两个和后两个 trimmed_history = sorted_history[2:-2] # Remove the first two and last two
avg_voltage = sum(trimmed_history) / len(trimmed_history) # 计算截尾平均 avg_voltage = sum(trimmed_history) / len(trimmed_history) # Calculate truncated mean
# 遍历电池曲线的每一段 # Traverse each segment of the battery curve
for (v1, p1), (v2, p2) in zip(curve, curve[1:]): for (v1, p1), (v2, p2) in zip(curve, curve[1:]):
# 如果电压在当前区间内 # If the voltage is within the current interval
if v2 <= avg_voltage <= v1: if v2 <= avg_voltage <= v1:
# 使用线性插值计算电量 # Use linear interpolation to calculate the percentage
return p2 + (p1 - p2) * (avg_voltage - v2) / (v1 - v2) return p2 + (p1 - p2) * (avg_voltage - v2) / (v1 - v2)
# 如果电压超出曲线范围,返回最低或最高电量 # If the voltage is out of the curve range, return the lowest or highest percentage
return curve[-1][1] if avg_voltage < curve[-1][0] else curve[0][1] return curve[-1][1] if avg_voltage < curve[-1][0] else curve[0][1]
def get_version(self): def get_version(self):
@ -307,7 +301,7 @@ class PiSugarServer:
If not PiSugar3, return None If not PiSugar3, return None
:return: Version string or None :return: Version string or None
""" """
if self.modle == 'PiSugar3': if self.model == 'PiSugar3':
try: try:
return bytes(self.i2creg[0xe2:0xee]).decode('ascii') return bytes(self.i2creg[0xe2:0xee]).decode('ascii')
except OSError as e: except OSError as e:
@ -321,7 +315,7 @@ class PiSugarServer:
:return: Model string. :return: Model string.
""" """
return self.modle return self.model
def get_battery_level(self): def get_battery_level(self):
""" """
@ -356,52 +350,52 @@ class PiSugarServer:
return self.allow_charging return self.allow_charging
def set_battery_allow_charging(self): def set_battery_allow_charging(self):
if self.modle == 'PiSugar3': if self.model == 'PiSugar3':
pass pass
elif self.modle == 'PiSugar2': elif self.model == 'PiSugar2':
# 禁止 gpio2 输出 # Disable gpio2 output
self._bus.write_byte_data(self.address, 0x54, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x54, self._bus.read_byte_data(
self.address, 0x54) & 0b11111011) self.address, 0x54) & 0b11111011)
# 开启充电 # Enable charging
self._bus.write_byte_data(self.address, 0x55, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x55, self._bus.read_byte_data(
self.address, 0x55) & 0b11111011) self.address, 0x55) & 0b11111011)
# 开启 gpio2 输出 # Enable gpio2 output
self._bus.write_byte_data(self.address, 0x54, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x54, self._bus.read_byte_data(
self.address, 0x54) | 0b00000100) self.address, 0x54) | 0b00000100)
elif self.modle == 'PiSugar2Plus': elif self.model == 'PiSugar2Plus':
# 禁止 gpio2 输出 # Disable gpio2 output
self._bus.write_byte_data(self.address, 0x56, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x56, self._bus.read_byte_data(
self.address, 0x56) & 0b11111011) self.address, 0x56) & 0b11111011)
# 开启充电 # Enable charging
self._bus.write_byte_data(self.address, 0x58, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x58, self._bus.read_byte_data(
self.address, 0x58) & 0b11111011) self.address, 0x58) & 0b11111011)
# 开启 gpio2 输出 # Enable gpio2 output
self._bus.write_byte_data(self.address, 0x56, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x56, self._bus.read_byte_data(
self.address, 0x56) | 0b00000100) self.address, 0x56) | 0b00000100)
return return
def set_battery_notallow_charging(self): def set_battery_notallow_charging(self):
if self.modle == 'PiSugar3': if self.model == 'PiSugar3':
pass pass
elif self.modle == 'PiSugar2': elif self.model == 'PiSugar2':
# 禁止 gpio2 输出 # Disable gpio2 output
print(self._bus.write_byte_data(self.address, 0x54, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x54, self._bus.read_byte_data(
self.address, 0x54) & 0b11111011)) self.address, 0x54) & 0b11111011)
# 关闭充电 # Disable charging
self._bus.write_byte_data(self.address, 0x55, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x55, self._bus.read_byte_data(
self.address, 0x55) | 0b00000100) self.address, 0x55) | 0b00000100)
# 开启 gpio2 输出 # Enable gpio2 output
self._bus.write_byte_data(self.address, 0x54, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x54, self._bus.read_byte_data(
self.address, 0x54) | 0b00000100) self.address, 0x54) | 0b00000100)
elif self.modle == 'PiSugar2Plus': elif self.model == 'PiSugar2Plus':
# 禁止 gpio2 输出 # Disable gpio2 output
self._bus.write_byte_data(self.address, 0x56, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x56, self._bus.read_byte_data(
self.address, 0x56) & 0b11111011) self.address, 0x56) & 0b11111011)
# 关闭充电 # Disable charging
self._bus.write_byte_data(self.address, 0x58, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x58, self._bus.read_byte_data(
self.address, 0x58) | 0b00000100) self.address, 0x58) | 0b00000100)
# 开启 gpio2 输出 # Enable gpio2 output
self._bus.write_byte_data(self.address, 0x56, self._bus.read_byte_data( self._bus.write_byte_data(self.address, 0x56, self._bus.read_byte_data(
self.address, 0x56) | 0b00000100) self.address, 0x56) | 0b00000100)