diff --git a/drivers/tildagon_power/bq25895/bq25895.c b/drivers/tildagon_power/bq25895/bq25895.c index de15df0..8f6f40b 100644 --- a/drivers/tildagon_power/bq25895/bq25895.c +++ b/drivers/tildagon_power/bq25895/bq25895.c @@ -48,7 +48,7 @@ static void write_scaled( bq_state_t* state, scaled_register_t scaledregister, f void bq_init( bq_state_t* state ) { write_bits( state, register_reset, 1 ); - uint8_t write_buffer[5] = { 0x02, 0x60, 0x1A, 0x18, 0x00 }; + uint8_t write_buffer[5] = { 0x02, 0x60, 0x10, 0x18, 0x00 }; mp_machine_i2c_buf_t buffer = { .len = 5, .buf = write_buffer }; tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); write_buffer[0] = 0x07; diff --git a/modules/bq25895.py b/modules/bq25895.py deleted file mode 100644 index bad3ed0..0000000 --- a/modules/bq25895.py +++ /dev/null @@ -1,514 +0,0 @@ -""" -BQ25895 power management ic driver in MicroPython. - -The driver supports basic read write access with some helpers - -This is a work in progress. - -""" - -from micropython import const -from collections import namedtuple - - -class charge_time: - Hrs5 = 0x00 - Hrs8 = 0x02 - Hrs12 = 0x04 - Hrs20 = 0x06 - - -class wdttime: - Disable = 0x00 - secs40 = 0x10 - secs80 = 0x20 - secs160 = 0x30 - - -class bq25895: - """ - BQ25895 driver class to allow access to registers - - Args: - i2c_instance (I2C) - - Attributes: - input_Ilim register entry for input current limit set by user or input source detection, range 100-3250mA, 50mA resolution - Ilim_pin register entry to enable or disable the current limit pin - enable_HiZ register entry to put the Vbus input into High impedance mode - input_voltage_limit_offset register entry for the input voltage limit offset, range 0-3100mV 100mV resolution - auto_dpdm register entry for automatic D+D- or PSEL detection - input_current_optimiser register entry to enable input current optimiser - boost_frequency register entry to set the boost converter frequency, 0 = 1.5MHz 1 = 500kHz(default) - conversion_rate register entry to enable the 1Hz sampling of ADCs - conversion_start register entry to start a single conversion - min_Vsys register entry to set the minimum system voltage limit range 3-3.7V resolution 0.1 - charge_enable register entry to enable the charge - otg_boost register entry to enable the boost converter - watchdog_reset register entry to reset the watchdog timer - battery_load register entry to enable the battery load - charge_Ilim register entry to set the fast charge current limit 0-5956mA 64mA resolution - enable_pulse_control register entry to enable the current pulse control - termination_current register entry for the termination current limit 64-1024mA, 64mA resolution - precharge_current register entry for the precharge current limit 64-1024mA, 64mA resolution - fast_charge_timer register entry for the fast charge timer setting, see charge_time for steps - charge_safety_timer_enable register entry to enable the charging safety timer - watchdog_timer_setting register entry for the watchdog timer setting, see wdttime for steps - status_led_disable register entry to disable the status LED - enable_termination register entry to enable charge termination - pulse_control_down register entry for Current pulse control voltage down enable - pulse_control_up register entry for Current pulse control voltage up enable - batfet_reset register entry for BATFET full system reset enable - batfet_delay_off register entry to enable a small delay before disabling the BATFET - batfet_disable register entry to disable the BATFET - timer_slowdown register entry to slow safety timer during dynamic power management - force_ICO register entry register entry to force start input current optimisation - boost_setpoint register entry for the boost target voltage 4.55-5.126V resolution 0.064V - Vsys_status register entry for Vsys regulation status - USB_input_status register entry for USB input status - power_good_status register entry for power good status - charge_status register entry for charging status - Vbus_status register entry for Vbus status - battery_fault register entry for battery fault - charge_fault register entry for charge fault - boost_fault register entry for boost converter fault - watchdog_fault register entry for watchdog fault - absolute_VINDPM_threshold register entry for absolute VINDPM threshold - force_abolute_threshold register entry to use relative or absolute VINDPM threshold - battery_voltage register entry for the Battery voltage ADC 2.304-4.48V resolution 0.02V - system_voltage register entry for the system voltage ADC 2.304-4.48V resolution 0.02V - Vbus_voltage register entry for the Vbus voltage ADC 2.6-15.3V resolution 0.1V - Vbus_good register entry for the Vbus good status - charge_current register entry for the Charge current ADC 0-6350mA resolution 50mA - DPM_current_limit register entry for the input current limit in effect while input current optimiser is enabled 100-3250mA resolution 50mA - IINDPM_status register entry for IINDPM status - VINDPM_status register entry for VINDPM status - ICO_status register entry For the Input current optimiser status - register_reset register entry to reset the registers - - """ - - def __init__(self, i2c_instance): - self.i2c = i2c_instance - - # address - ADDRESS = const(0x6A) - - Register = namedtuple("Register", ["register", "mask", "position"]) - ScaledRegister = namedtuple( - "ScaledRegister", ["register", "mask", "position", "scaling", "offset"] - ) - - # reg 00 - # input current limit, changed by input source type detection default 500mA - input_Ilim = ScaledRegister(0x00, 0x3F, 0, 50, 100) - # ILim pin enable/disable - Ilim_pin = Register(0x00, 0x40, 6) - enable_HiZ = Register(0x00, 0x80, 7) - # reg 01 - # boost temperature threshold not used - # input voltage limit - input_voltage_limit_offset = ScaledRegister(0x01, 0x1F, 0, 100, 0) - # reg 02 - # auto_dpdm = Register( 0x02, 0x01, 0 ) - # the following use D+/D- to detect input and these are not connected - # force_dpdm = Register(0x02, 0x02, 1 ) - # max_charge_adapter = Register( 0x02, 0x04, 2 ) - # hv_dcp = Register( 0x02, 0x08, 3 ) - input_current_optimiser = Register(0x02, 0x10, 4) - boost_frequency = Register(0x02, 0x20, 5) - conversion_rate = Register(0x02, 0x40, 6) - conversion_start = Register(0x02, 0x80, 7) - # reg 03 - min_Vsys = ScaledRegister(0x03, 0x0E, 1, 3.0, 0.1) - charge_enable = Register(0x03, 0x10, 4) - otg_boost = Register(0x03, 0x20, 5) - watchdog_reset = Register(0x03, 0x40, 6) - battery_load = Register(0x03, 0x80, 7) - # reg 04 - # The battery is a 2000mAh which is the default Charge current limit - # charge_Ilim = ScaledRegister( 0x04, 0x7F, 0, 64, 0 ) - # PD being handled by FUSB - # enable_pulse_control = Register( 0x04, 0x80, 7 ) - # reg 05 - # defaults for these are ok. - # termination_current = ScaledRegister( 0x05, 0x0F, 0, 64, 64 ) - # precharge_current = ScaledRegister( 0x05, 0xF0, 4, 64, 64 ) - # reg 06 - we don't want to change this - # recharge_threshold_offset = Register( 0x06, 0x01, 0, 0, 0 ) - # precharge_threshold = Register( 0x06, 0x01, 0, 0 ,0 ) - # charge_voltage_limit = Register( 0x06, 0xFC, 2, 0.016, 3.84 ) - # reg 07 - fast_charge_timer = Register(0x07, 0x06, 1) - charge_safety_timer_enable = Register(0x07, 0x08, 3) - watchdog_timer_setting = Register(0x07, 0x30, 4) - status_led_disable = Register(0x07, 0x40, 6) - enable_termination = Register(0x07, 0x80, 7) - # reg 08 - # we don't use the thermistor - # we don't use the resistance compensation as we don't control the battery leads. - # vclamp = Register( 0x08, 0x1C, 2, 32, 0 ) - # compensation_offset = Register(0x08, 0xE0, 5, 20, 0 ) - # reg 09 - # PD being handled by FUSB - # pulse_control_down = Register( 0x09, 0x01, 0 ) - # pulse_control_up = Register( 0x09, 0x02, 1 ) - batfet_reset = Register(0x09, 0x04, 2) - batfet_delay_off = Register(0x09, 0x08, 3) - batfet_disable = Register(0x09, 0x20, 5) - # we don't do DPM or thermal regulation - # timer_slowdown = Register( 0x09, 0x40, 6 ) - force_ICO = Register(0x09, 0x80, 7) - # reg 0A - # we want this at the default - # boost_setpoint = ScaledRegister( 0x0A, 0xF0, 4, 4.55, 0.0064 ) - # reg 0B read only - Vsys_status = Register(0x0B, 0x01, 0) - USB_input_status = Register(0x0B, 0x02, 1) - power_good_status = Register(0x0B, 0x04, 2) - charge_status = Register(0x0B, 0x18, 3) - Vbus_status = Register(0x0B, 0xE0, 5) - # reg 0C read only - # thermistor not used - battery_fault = Register(0x0C, 0x08, 3) - charge_fault = Register(0x0C, 0x30, 4) - boost_fault = Register(0x0C, 0x40, 6) - watchdog_fault = Register(0x0C, 0x80, 7) - # reg 0D read only - absolute_VINDPM_threshold = ScaledRegister(0x0D, 0x7F, 0, 0.2, 2.6) - force_abolute_threshold = Register(0x0D, 0x80, 7) - # reg 0E read only - battery_voltage = ScaledRegister(0x0E, 0x7F, 0, 0.02, 2.304) - # thermal status not used - # reg 0F read only - system_voltage = ScaledRegister(0x0F, 0x7F, 0, 0.02, 2.304) - # reg 10 read only - # thermistor not used - # reg 11 read only - Vbus_voltage = ScaledRegister(0x11, 0x7F, 0, 0.1, 2.6) - Vbus_good = Register(0x11, 0x80, 7) - # reg 12 read only - charge_current = ScaledRegister(0x12, 0x7F, 0, 0.05, 0) - # reg 13 read only - DPM_current_limit = ScaledRegister(0x13, 0x3F, 0, 50, 100) - IINDPM_status = Register(0x13, 0x40, 6) - VINDPM_status = Register(0x13, 0x80, 7) - # reg 14 - # device_revision = Register( 0x14, 0x03, 0 ) always reads 01 - # temperature profile not used - # part_no = Register( 0x14, 0x38, 3 ) always reads 111 - ICO_status = Register(0x14, 0x40, 3) - register_reset = Register(0x14, 0x80, 7) - - # bit lists - charging_status_list = [ - "Not Charging", - "Pre-Charging", - "Fast Charging", - "Terminated", - ] - Vbus_status_list = [ - "No Input", - "USB Host SDP", - "USB CDP", - "USB DCP", - "Adjustable HV DCP", - "Unkown Adapter", - "Non Standard Adapter", - "OTG", - ] - USB_input_status_list = ["USB 100", "USB 500"] - Vsys_regulation_status_list = ["Not in Vsys Regulation", "In Vsys Regulation"] - power_good_status_list = ["Power not good", "Power good"] - battery_fault_list = ["Normal", "Battery Over Voltage"] - charge_fault_list = ["Normal", "Input Fault", "Thermal Shutdown", "Safety Timer"] - boost_fault_list = ["Normal", "Overloaded, Over Voltage or Battery Low"] - watchdog_fault_list = ["Normal", "Timer Expired"] - fault_dict = dict( - [ - ("Battery", battery_fault_list), - ("Charge", charge_fault_list), - ("Boost", boost_fault_list), - ("Watchdog", watchdog_fault_list), - ] - ) - status_dict = dict( - [ - ("VsysRegulation", Vsys_regulation_status_list), - ("USB input", USB_input_status_list), - ("power good", power_good_status_list), - ("Charge", charging_status_list), - ("Vbus", Vbus_status_list), - ] - ) - - # helpers - def read_bits(self, register): - """ - Returns a value from selected bits of a register - - Args: - register(Register): a namedtuple containing - -register - -mask - -position - :rtype: (int) - """ - regVal = self.i2c.readfrom_mem(self.ADDRESS, register.register, 1)[0] - return (regVal & register.mask) >> register.position - - def write_bits(self, register, value): - """ - Writes a value to selected bits of a register - - Args: - register(Register): a namedtuple containing - -register - -mask - -position - """ - regVal = self.i2c.readfrom_mem(self.ADDRESS, register.register, 1)[0] - regVal = regVal & (~register.mask) - regVal = regVal | (value << register.position) - self.i2c.writeto_mem(self.ADDRESS, register.register, bytes([regVal])) - - def read_scaled(self, scaledregister): - """ - Returns a value from a register entry and applies scaling and offset. - - Args: - scaledregister(ScaledRegister): a namedtuple containing - -register - -mask - -position - -scaling - -offset - :rtype: (float) - """ - regVal = self.i2c.readfrom_mem(self.ADDRESS, scaledregister.register, 1)[0] - return ( - float((regVal & scaledregister.mask) >> scaledregister.position) - * scaledregister.scaling - ) + scaledregister.offset - - def write_scaled(self, scaledregister, value): - """ - write a scaled value to a register entry. - - Args: - scaledregister(ScaledRegister): a namedtuple containing - -register - -mask - -position - -scaling - -offset - value(float): value to be scaled - """ - regVal = self.i2c.readfrom_mem(self.ADDRESS, scaledregister.register, 1)[0] - regVal = regVal & (~scaledregister.mask) - temp = ( - int((value - scaledregister.offset) / scaledregister.scaling) - << scaledregister.position - ) & scaledregister.mask - regVal = regVal | temp - self.i2c.writeto_mem(self.ADDRESS, scaledregister.register, bytes([regVal])) - - def enable_conversion(self, enable=True, single=False): - """ - Start a one shot conversion of the ADCs or enable or disable the conversion at a 1Hz rate - - Args: - enable (bool): if True enable the conversion. - if False disable the converter. - not used for if single is True. - single (bool): if True start a single ADC conversion - """ - if single: - self.write_bits(self.conversion_rate, 1) - else: - if enable: - self.write_bits(self.conversion_start, 1) - else: - self.write_bits(self.conversion_start, 0) - - def enable_HiZ_input(self, enable): - """ - Put the converter into High impedance mode on the input to prevent current draw or take it out of HiZ mode - - Args: - enable (bool): if True enable the high impedance mode - if False disable the high impedance mode - """ - if enable: - self.write_bits(self.enable_HiZ, 1) - else: - self.write_bits(self.enable_HiZ, 0) - - def enable_boost(self, enable=True): - """ - Control the boost output - - Args: - enable (bool): if True enable boost converter - if False disable the boost converter - """ - if enable: - self.write_bits(self.otg_boost, 1) - else: - self.write_bits(self.otg_boost, 0) - - def disconnect_battery(self): - """ - Disconnect the battery from the IC - """ - self.write_bits(self.batfet_disable, 1) - - def connect_battery(self): - """ - Connect the battery to the IC - """ - self.write_bits(self.batfet_disable, 0) - - def set_input_current_limit(self, limit): - """ - Set the Input current limit - - Args: - limit (int): Limit in mA, range 100-3250mA resolution 50mA - """ - self.write_scaled(self.input_Ilim, limit) - - def get_status(self): - """ - Returns the decoded status read from the device - Do not use from an ISR - - :rtype: (dict) - """ - read = self.i2c.readfrom_mem(self.ADDRESS, 0x0B, 1)[0] - status = dict( - [ - ("VsysRegulation", 0), - ("USB input", 0), - ("power good", 0), - ("Charge", 0), - ("Vbus", 0), - ] - ) - status["VsysRegulation"] = ( - read & self.Vsys_status.mask - ) >> self.Vsys_status.position - status["USB input"] = ( - read & self.USB_input_status.mask - ) >> self.USB_input_status.position - status["power good"] = ( - read & self.power_good_status.mask - ) >> self.power_good_status.position - status["Charge"] = ( - read & self.charge_status.mask - ) >> self.charge_status.position - status["Vbus"] = (read & self.Vbus_status.mask) >> self.Vbus_status.position - return status - - def get_fault(self): - """ - Returns the decoded faults, reads the register twice to ensure previous faults are cleared - Do not use from an ISR - - :rtype: (dict) - """ - read = self.i2c.readfrom_mem(self.ADDRESS, 0x0C, 1)[0] - read = self.i2c.readfrom_mem(self.ADDRESS, 0x0C, 1)[0] - fault = dict([("Battery", 0), ("Charge", 0), ("Boost", 0), ("Watchdog", 0)]) - fault["Battery"] = ( - read & self.battery_fault.mask - ) >> self.battery_fault.position - fault["Charge"] = (read & self.charge_fault.mask) >> self.charge_fault.position - fault["Boost"] = (read & self.boost_fault.mask) >> self.boost_fault.position - fault["Watchdog"] = ( - read & self.watchdog_fault.mask - ) >> self.watchdog_fault.position - return fault - - def get_Vbat(self): - """ - Returns the battery Voltage - - :rtype: (float) - """ - return self.read_scaled(self.battery_voltage) - - def get_Vsys(self): - """ - Returns the System output Voltage - - :rtype: (float) - """ - return self.read_scaled(self.system_voltage) - - def get_Vbus(self): - """ - Returns the Vbus input Voltage - - :rtype: (float) - """ - return self.read_scaled(self.Vbus_voltage) - - def get_DPM_current_limit(self): - """ - Returns current limit determined by dynamic power management - - :rtype: (float) - """ - return self.read_scaled(self.DPM_current_limit) - - def reset(self): - """ - Reset the device to a known state. - """ - self.write_bits(self.register_reset, 1) - - def init(self): - """ - Initialise the bq25895 to the state we want - """ - # reset ic to known state - self.reset() - # use single byte writes to reduce I2C traffic instead of read modify write since we just reset registers to known values - # Leave input current limit (REG0x00 at 500mA until PD is complete - # Disable the boost output, leave minimum system voltage at 3.5V and charging enabled - self.i2c.writeto_mem(self.ADDRESS, 0x03, bytes([0x1A])) - # start ADC conversions running at a 1s interval and disable detection using D+/D- and ICO - self.i2c.writeto_mem(self.ADDRESS, 0x02, bytes([0x60])) - # disable the watchdog to allow charging while apps have control - self.i2c.writeto_mem(self.ADDRESS, 0x07, bytes([0x8C])) - - -if __name__ == "__main__": - from machine import Pin, I2C - from utime import sleep_ms - - i2c = I2C(7) - pin_reset_i2c = Pin(9, Pin.OUT) - pin_reset_i2c.on() - pmic = bq25895(i2c) - pmic.init() - # start 1Hz ADC sampling - pmic.enable_conversion() - # report status and faults with various methods - print("Status" + str(pmic.get_status())) - status = pmic.get_status() - for key in status: - print(key + " " + str(pmic.status_dict[key][status[key]])) - Faults = pmic.get_fault() - for key in Faults: - print(key + " " + str(pmic.fault_dict[key][Faults[key]])) - print("Battery still " + str(pmic.battery_fault_list[Faults["Battery"]])) - # let the ADC run at least once - sleep_ms(1000) - # read the voltages - print("Battery Voltage = " + str(pmic.get_Vbat()) + "V") - print("System Voltage = " + str(pmic.get_Vsys()) + "V") - print("Vbus Voltage = " + str(pmic.get_Vbus()) + "V") - # read the current - print("DPM current limit = " + str(pmic.get_DPM_current_limit()) + "mA") diff --git a/modules/firmware_apps/poweroff.py b/modules/firmware_apps/poweroff.py index d289683..d46746a 100644 --- a/modules/firmware_apps/poweroff.py +++ b/modules/firmware_apps/poweroff.py @@ -18,10 +18,8 @@ async def run(self, render_update): # Wait for an answer from the dialogue, and if it was yes, randomise colour if await dialog.run(render_update): - import machine - import bq25895 - - bq25895.bq25895(machine.I2C(7)).disconnect_battery() + import power + power.Off() self.off = True else: self.minimise() diff --git a/modules/fusb302b.py b/modules/fusb302b.py deleted file mode 100644 index 175463d..0000000 --- a/modules/fusb302b.py +++ /dev/null @@ -1,839 +0,0 @@ -""" -FUSB302 Programmable USB Type-C Controller w/PD driver in MicroPython. - -The driver supports basic read write access with some helpers - -This is a work in progress. - -""" - -from micropython import const -from collections import namedtuple -from utime import sleep - - -class fusb302: - """ - FUSB302 driver class to allow access to registers - - Args: - i2c_instance (I2C) - - Attributes: - lots of register entries... - - """ - - def __init__(self, i2c_instance): - self.i2c = i2c_instance - - # address - ADDRESS = const(0x22) - - Register = namedtuple("Register", ["register", "mask", "position"]) - ScaledRegister = namedtuple( - "ScaledRegister", ["register", "mask", "position", "scaling", "offset"] - ) - - # reg 01 Device ID - revision_id = Register(0x01, 0x0F, 0) - version_id = Register(0x01, 0xF0, 4) - # reg 02 Switches0 - pulldown_cc1 = Register(0x02, 0x01, 0) - pulldown_cc2 = Register(0x02, 0x02, 1) - measure_cc = Register(0x02, 0x0C, 2) - Vconn_on_cc1 = Register(0x02, 0x10, 4) - Vconn_on_cc2 = Register(0x02, 0x20, 5) - host_current_on_cc1 = Register(0x02, 0x40, 6) - host_current_on_cc2 = Register(0x02, 0x80, 7) - # reg 03 Switches1 - enable_bmc_cc1 = Register(0x03, 0x01, 0) - enable_bmc_cc2 = Register(0x03, 0x02, 1) - auto_crc = Register(0x03, 0x04, 2) - datarole = Register(0x03, 0x10, 4) - spec_revision = Register(0x03, 0x60, 5) - power_role = Register(0x03, 0x80, 7) - # reg 04 Measure - measurement_Vcc = ScaledRegister(0x04, 0x3F, 0, 42, 42) - measurement_Vbus = ScaledRegister(0x04, 0x3F, 0, 42, 420) - measure_Vbus = Register(0x04, 0x40, 6) - # reg 05 slice - bmc_slicer = Register(0x05, 0x1F, 0) - slicer_hysteresis = Register(0x05, 0xC0, 6) - # reg 06 control 0 - start_transmitter = Register(0x06, 0x01, 0) - autostart_tx_on_crc = Register(0x06, 0x02, 1) - host_current = Register(0x06, 0x0C, 2) - interrupt_mask = Register(0x06, 0x20, 4) - tx_flush = Register(0x06, 0x40, 6) - # reg 07 control 1 - enable_sop = Register(0x07, 0x01, 0) - enable_sop_double = Register(0x07, 0x02, 1) - rx_flush = Register(0x07, 0x04, 2) - bist_pattern = Register(0x07, 0x10, 4) - enable_sop_debug = Register(0x07, 0x20, 5) - enable_sop_debug_double = Register(0x07, 0x40, 6) - # reg 08 control 2 - enable_toggle = Register(0x08, 0x01, 0) - toggle_mode = Register(0x08, 0x06, 1) - enable_wake = Register(0x08, 0x08, 3) - enable_toggle_read_only = Register(0x08, 0x20, 5) - toggle_powersave_mode = Register(0x08, 0xC0, 6) - # reg 09 control 3 - auto_retry = Register(0x09, 0x01, 0) - retries_no = Register(0x09, 0x06, 2) - auto_softreset = Register(0x09, 0x08, 3) - auto_hardreset = Register(0x09, 0x01, 4) - send_hardreset = Register(0x09, 0x40, 6) - # reg 0A - mask_bc_level = Register(0x0A, 0x01, 0) - mask_collission = Register(0x0A, 0x02, 1) - mask_wake = Register(0x0A, 0x02, 2) - mask_alert = Register(0x0A, 0x04, 3) - mask_crc = Register(0x0A, 0x10, 4) - mask_comparator_change = Register(0x0A, 0x20, 5) - mask_activity = Register(0x0A, 0x40, 6) - mask_vbus_ok = Register(0x0A, 0x80, 7) - # reg 0B power control - enable_oscillator = Register(0x0B, 0x01, 0) - enable_measure = Register(0x0B, 0x02, 1) - enable_rx_and_ref = Register(0x0B, 0x04, 2) - enable_bandgap_and_wake = Register(0x0B, 0x08, 3) - # reg 0C reset - register_reset = Register(0x0C, 0x01, 0) - pd_reset = Register(0x0C, 0x02, 1) - # reg 0D ocp - ocp_cuurent = ScaledRegister(0x0D, 0x07, 0, 10, 10) - ocp_cuurent10 = ScaledRegister(0x0D, 0x07, 0, 100, 100) - ocp_range = Register(0x0D, 0x80, 3) - # reg 0E mask a - mask_hardreset_int = Register(0x0E, 0x01, 0) - mask_softreset_int = Register(0x0E, 0x02, 1) - mask_tx_sent_int = Register(0x0E, 0x04, 2) - mask_hard_sent_int = Register(0x0E, 0x08, 3) - mask_retry_fail_int = Register(0x0E, 0x10, 4) - mask_soft_fail_int = Register(0x0E, 0x20, 5) - mask_toggle_done_int = Register(0x0E, 0x40, 6) - mask_ocp_temp_int = Register(0x0E, 0x80, 7) - # reg 0F mask b - mask_good_crc_sent_int = Register(0x0F, 0x01, 0) - # reg 10 Control4 - toggle_unattahce_exit = Register(0x10, 0x01, 0) - # reg 3C status 0a read only - hard_reset_order = Register(0x3C, 0x01, 0) - soft_reset_order = Register(0x3C, 0x02, 1) - power_state = Register(0x3C, 0x0C, 2) - retry_fail = Register(0x3C, 0x10, 4) - soft_fail = Register(0x3C, 0x20, 5) - # reg 3D status 1a - rx_sop = Register(0x3D, 0x01, 0) - rx_sop_debug = Register(0x3D, 0x02, 1) - rx_sop_double_debug = Register(0x3D, 0x04, 2) - toggle_status = Register(0x3D, 0x38, 3) - # reg 3E interrupt a - hardreset_int = Register(0x3E, 0x01, 0) - softreset_int = Register(0x3E, 0x02, 1) - tx_sent_int = Register(0x3E, 0x04, 2) - hard_sent_int = Register(0x3E, 0x08, 3) - retry_fail_int = Register(0x3E, 0x01, 4) - soft_fail_int = Register(0x3E, 0x02, 5) - toggle_done_int = Register(0x3E, 0x04, 6) - ocp_temp_int = Register(0x3E, 0x08, 7) - # reg 3F interrupt b - good_crc_sent_int = Register(0x3F, 0x01, 0) - # reg 40 status0 - bc_level = Register(0x40, 0x03, 0) - wake = Register(0x40, 0x04, 2) - alert = Register(0x40, 0x08, 3) - crc_check = Register(0x40, 0x10, 4) - comparator = Register(0x40, 0x20, 5) - activity = Register(0x40, 0x40, 6) - vbus_ok = Register(0x40, 0x80, 7) - # reg 41 status 1 - ocp = Register(0x41, 0x01, 0) - over_temperature = Register(0x41, 0x02, 1) - tx_fifo_full = Register(0x41, 0x04, 2) - tx_fifo_empty = Register(0x41, 0x08, 3) - rx_fifo_full = Register(0x41, 0x10, 4) - rx_fifo_empty = Register(0x41, 0x20, 5) - rx_sop_prime = Register(0x41, 0x40, 6) - rx_sop_prime_double = Register(0x41, 0x80, 7) - # reg 42 interrupt - bc_level_int = Register(0x42, 0x01, 0) - colision_int = Register(0x42, 0x02, 1) - wake_int = Register(0x42, 0x04, 2) - alert_int = Register(0x42, 0x08, 3) - crc_check_int = Register(0x42, 0x10, 4) - compare_change_int = Register(0x42, 0x20, 5) - activity_int = Register(0x42, 0x40, 6) - vbus_ok_int = Register(0x42, 0x80, 7) - # reg 43 fifo access - rxtx_fifo = Register(0x43, 0xFF, 0) - - # helpers - def read_bits(self, register): - """ - Returns a value from selected bits of a register - - Args: - register(Register): a namedtuple containing - -register - -mask - -position - :rtype: (int) - """ - regVal = self.i2c.readfrom_mem(self.ADDRESS, register.register, 1)[0] - return (regVal & register.mask) >> register.position - - def write_bits(self, reg, value): - """ - Writes a value to selected bits of a register - - Args: - register(Register): a namedtuple containing - -register - -mask - -position - """ - regVal = self.i2c.readfrom_mem(self.ADDRESS, reg.register, 1)[0] - regVal = regVal & (~reg.mask) - regVal = regVal | (value << reg.position) - self.i2c.writeto_mem(self.ADDRESS, reg.register, bytes([regVal])) - - def read_scaled(self, scaledregister): - """ - Returns a value from a register entry and applies scaling and offset. - - Args: - scaledregister(ScaledRegister): a namedtuple containing - -register - -mask - -position - -scaling - -offset - :rtype: (float) - """ - regVal = self.i2c.readfrom_mem(self.ADDRESS, scaledregister.register, 1)[0] - return ( - float((regVal & scaledregister.mask) >> scaledregister.position) - * scaledregister.scaling - ) + scaledregister.offset - - def write_scaled(self, scaledregister, value): - """ - write a scaled value to a register entry. - - Args: - scaledregister(ScaledRegister): a namedtuple containing - -register - -mask - -position - -scaling - -offset - value(float): value to be scaled - """ - regVal = self.i2c.readfrom_mem(self.ADDRESS, scaledregister.register, 1)[0] - regVal = regVal & (~scaledregister.mask) - temp = ( - int((value - scaledregister.offset) / scaledregister.scaling) - << scaledregister.position - ) & scaledregister.mask - regVal = regVal | int(temp) - self.i2c.writeto_mem(self.ADDRESS, scaledregister.register, bytes([regVal])) - - def set_bit(self, register, input_byte, value): - """ - Set bit or bits of the input byte and return. - - Args: - scaledregister(Register): a namedtuple containing - -register - -mask - -position - input_byte(int): the byte to combine with the scaled data - value(float): value to be scaled - - :rtype: (int) - """ - return (input_byte & (~register.mask)) | (value << register.position) - - def set_scaled(self, scaledregister, input_byte, value): - """ - Scale a value to the register entries scaling and set the bits in the byte - - Args: - scaledregister(ScaledRegister): a namedtuple containing - -register - -mask - -position - -scaling - -offset - input_byte(int): the byte to combine with the scaled data - value(float): value to be scaled - - :rtype: (int) - """ - temp = ( - int((value - scaledregister.offset) / scaledregister.scaling) - << scaledregister.position - ) & scaledregister.mask - return (input_byte & (~scaledregister.mask)) | temp - - def reset(self): - """ - Reset the registers to a known state - """ - self.write_bits(self.register_reset, 1) - - def reset_PD(self): - """ - Reset the PD logic - """ - self.write_bits(self.pd_reset, 1) - - def flush_tx(self): - """ - Flush the transmit buffer - """ - self.write_bits(self.tx_flush, 1) - - def flush_rx(self): - """ - Flush the receive buffer - """ - self.write_bits(self.rx_flush, 1) - - def rx_empty(self): - """ - are bytes available - - :rtype: (int) - """ - return self.read_bits(self.rx_fifo_empty) - - def power_up(self): - """ - Turn on - [0] Bandgap and wake circuit. - [1]: Receiver powered and current references for Measure block - [2]: Measure block powered. - [3]: Enable internal oscillator. - """ - self.i2c.writeto_mem( - self.ADDRESS, self.enable_oscillator.register, bytes([0x0F]) - ) - - def get_status0(self): - """ - Returns the decoded status0 read from the device - Do not use from an ISR - - :rtype: (dict) - """ - read = self.i2c.readfrom_mem(self.ADDRESS, self.vbus_ok.register, 1)[0] - status = dict( - [ - ("VBUSOK", 0), - ("ACTIVITY", 0), - ("COMP", 0), - ("CRC_CHK", 0), - ("ALERT", 0), - ("WAKE", 0), - ("BC_LVL", 0), - ] - ) - status["VBUSOK"] = (read & self.vbus_ok.mask) >> self.vbus_ok.position - status["ACTIVITY"] = (read & self.activity.mask) >> self.activity.position - status["COMP"] = (read & self.comparator.mask) >> self.comparator.position - status["CRC_CHK"] = (read & self.crc_check.mask) >> self.crc_check.position - status["ALERT"] = (read & self.alert.mask) >> self.alert.position - status["WAKE"] = (read & self.wake.mask) >> self.wake.position - status["BC_LVL"] = (read & self.bc_level.mask) >> self.bc_level.position - return status - - def get_status1(self): - """ - Returns the decoded status1 read from the device - Do not use from an ISR - - :rtype: (dict) - """ - read = self.i2c.readfrom_mem(self.ADDRESS, self.vbus_ok.register, 1)[0] - status = dict( - [ - ("OCP", 0), - ("OVRTEMP", 0), - ("TX_FULL", 0), - ("TX_EMPTY", 0), - ("RX_FULL", 0), - ("RX_EMPTY", 0), - ("RXSOP1", 0), - ("RXSOP2", 0), - ] - ) - status["OCP"] = (read & self.ocp.mask) >> self.ocp.position - status["OVRTEMP"] = ( - read & self.over_temperature.mask - ) >> self.over_temperature.position - status["TX_FULL"] = ( - read & self.tx_fifo_full.mask - ) >> self.tx_fifo_full.position - status["TX_EMPTY"] = ( - read & self.tx_fifo_empty.mask - ) >> self.tx_fifo_empty.position - status["RX_FULL"] = ( - read & self.rx_fifo_full.mask - ) >> self.rx_fifo_full.position - status["RX_EMPTY"] = ( - read & self.rx_fifo_empty.mask - ) >> self.rx_fifo_empty.position - status["RXSOP1"] = (read & self.rx_sop_prime.mask) >> self.rx_sop_prime.position - status["RXSOP2"] = ( - read & self.rx_sop_prime_double.mask - ) >> self.rx_sop_prime_double.position - return status - - def get_status0a(self): - """ - Returns the decoded status0a read from the device - Do not use from an ISR - - :rtype: (dict) - """ - read = self.i2c.readfrom_mem(self.ADDRESS, self.vbus_ok.register, 1)[0] - status = dict( - [ - ("HARDRST", 0), - ("SOFTRST", 0), - ("POWER", 0), - ("RETRYFAIL", 0), - ("SOFTFAIL", 0), - ] - ) - status["HARDRST"] = ( - read & self.hard_reset_order.mask - ) >> self.hard_reset_order.position - status["SOFTRST"] = ( - read & self.soft_reset_order.mask - ) >> self.soft_reset_order.position - status["POWER"] = (read & self.power_state.mask) >> self.power_state.position - status["RETRYFAIL"] = (read & self.retry_fail.mask) >> self.retry_fail.position - status["SOFTFAIL"] = (read & self.soft_fail.mask) >> self.soft_fail.position - return status - - def get_status1a(self): - """ - Returns the decoded status1a read from the device - Do not use from an ISR - - :rtype: (dict) - """ - read = self.i2c.readfrom_mem(self.ADDRESS, self.vbus_ok.register, 1)[0] - status = dict( - [ - ("RXSOP", 0), - ("RXSOP1DB", 0), - ("RXSOP2DB", 0), - ("TOGSS", 0), - ] - ) - status["RXSOP"] = (read & self.rx_sop.mask) >> self.ocp.position - status["RXSOP1DB"] = ( - read & self.rx_sop_debug.mask - ) >> self.rx_sop_debug.position - status["RXSOP2DB"] = ( - read & self.rx_sop_double_debug.mask - ) >> self.rx_sop_double_debug.position - status["TOGSS"] = ( - read & self.toggle_status.mask - ) >> self.toggle_status.position - return status - - def get_interrupts(self): - """ - amalgamate the interrupts and return a dict containing a flag for each - Do not use from an ISR... - - :rtype: (dict) - """ - Interrupta = self.i2c.readfrom_mem( - self.ADDRESS, self.hardreset_int.register, 1 - )[0] - Interruptb = self.i2c.readfrom_mem( - self.ADDRESS, self.good_crc_sent_int.register, 1 - )[0] - Interrupt = self.i2c.readfrom_mem(self.ADDRESS, self.bc_level_int.register, 1)[ - 0 - ] - current_interrupts = dict( - [ - ("I_HARDRST", 0), - ("I_SOFTRST", 0), - ("I_TXSENT", 0), - ("I_HARDSENT", 0), - ("I_RETRYFAIL", 0), - ("I_SOFTFAIL", 0), - ("I_TOGDONE", 0), - ("I_OCP_TEMP", 0), - ("I_GCRCSENT", 0), - ("I_BC_LVL", 0), - ("I_COLLISION", 0), - ("I_WAKE", 0), - ("I_ALERT", 0), - ("I_CRC_CHK", 0), - ("I_COMP_CHNG", 0), - ("I_ACTIVITY", 0), - ("I_VBUSOK", 0), - ] - ) - current_interrupts["I_HARDRST"] = ( - Interrupta & self.hardreset_int.mask - ) >> self.hardreset_int.position - current_interrupts["I_SOFTRST"] = ( - Interrupta & self.softreset_int.mask - ) >> self.softreset_int.position - current_interrupts["I_TXSENT"] = ( - Interrupta & self.tx_sent_int.mask - ) >> self.tx_sent_int.position - current_interrupts["I_HARDSENT"] = ( - Interrupta & self.hard_sent_int.mask - ) >> self.hard_sent_int.position - current_interrupts["I_RETRYFAIL"] = ( - Interrupta & self.retry_fail_int.mask - ) >> self.retry_fail_int.position - current_interrupts["I_SOFTFAIL"] = ( - Interrupta & self.soft_fail_int.mask - ) >> self.soft_fail_int.position - current_interrupts["I_OCP_TEMP"] = ( - Interrupta & self.ocp_temp_int.mask - ) >> self.ocp_temp_int.position - current_interrupts["I_TOGDONE "] = ( - Interrupta & self.toggle_done_int.mask - ) >> self.toggle_done_int.position - current_interrupts["I_GCRCSENT"] = ( - Interruptb & self.good_crc_sent_int.mask - ) >> self.good_crc_sent_int.position - current_interrupts["I_BC_LVL"] = ( - Interrupt & self.bc_level_int.mask - ) >> self.bc_level_int.position - current_interrupts["I_COLLISION"] = ( - Interrupt & self.colision_int.mask - ) >> self.colision_int.position - current_interrupts["I_WAKE"] = ( - Interrupt & self.wake_int.mask - ) >> self.wake_int.position - current_interrupts["I_ALERT"] = ( - Interrupt & self.alert_int.mask - ) >> self.alert_int.position - current_interrupts["I_CRC_CHK"] = ( - Interrupt & self.crc_check_int.mask - ) >> self.crc_check_int.position - current_interrupts["I_COMP_CHNG"] = ( - Interrupt & self.compare_change_int.mask - ) >> self.compare_change_int.position - current_interrupts["I_ACTIVITY"] = ( - Interrupt & self.activity_int.mask - ) >> self.activity_int.position - current_interrupts["I_VBUSOK"] = ( - Interrupt & self.vbus_ok_int.mask - ) >> self.vbus_ok_int.position - return current_interrupts - - def set_overcurrent_protection(self): - """ - Set the over current protection level - """ - - def determine_input_current_limit(self): - """ - Determine the input current limit - To be called on attach ( VbusOK rising edge ) before comms starts - """ - # disable Vbus measurement and set CC threshold - self.i2c.writeto_mem(self.ADDRESS, 0x04, bytes([0x3E])) - # measure CC1 - self.write_bits(self.measure_cc, 1) - self.cc_select = 1 - sleep(0.001) - status = self.get_status0() - if status["BC_LVL"] == 0: - # Ra connected to this CC, change to other CC connection - self.cc_select = 2 - self.write_bits(self.measure_cc, 2) - sleep(0.001) - # re-read status - status = self.get_status0() - # determine current level. - self.input_current_limit = 500 - if status["BC_LVL"] > 0: - if status["BC_LVL"] == 2: - self.input_current_limit = 1500 - elif status["BC_LVL"] == 3 and status["COMP"] == 0: - self.input_current_limit = 3000 - else: - # what should this be - self.input_current_limit = 500 - self.setup_pd() - - def reset_input_current_limit(self): - """ - Reset the input current limit on detach - To be called on detach (VbusOK falling edge) - """ - self.input_current_limit = 500 - - def get_input_current_limit(self): - """ - Get the input current limit - - :rtype: (int) current in mA - """ - return self.input_current_limit - - def attached(self): - """ - get the attached status - """ - status = self.get_Status0() - if self.host: - return status["COMP"] == 0 - else: - return status["VBUSOK"] == 1 - - def setup_device(self): - """ - Initialise the fusb302 to a device - - This should be called before any BMC registers are changed. - """ - # put device into a known state, including toggle off. - self.reset() - self.power_up() - # set comp threshold to 2.226V for CC determination and enable Vbus measurement for VbusOK interrupt - self.i2c.writeto_mem(self.ADDRESS, 0x04, bytes([0x7E])) - # setup over current protection - - # register? an isr for the VbusOk for attach detection - - self.input_current_limit = 500 - self.cc_select = 0 - # setup 3 auto retries for PD - self.i2c.writeto_mem(self.ADDRESS, 0x09, bytes([0x07])) - # unmask Vbus ok, OCP and good CRC sent interrupts - self.i2c.writeto_mem(self.ADDRESS, 0x0A, bytes([0x7F])) - self.i2c.writeto_mem(self.ADDRESS, 0x0E, bytes([0x7F])) - self.i2c.writeto_mem(self.ADDRESS, 0x0F, bytes([0x00])) - # flush buffers and enable interrupts - self.i2c.writeto_mem(self.ADDRESS, 0x06, bytes([0x64])) - self.i2c.writeto_mem(self.ADDRESS, 0x07, bytes([0x04])) - - # set bits for good crc and auto response last as we need to respond within a timeout. - self.i2c.writeto_mem(self.ADDRESS, 0x03, bytes([0x24])) - self.host = False - - def setup_host(self): - """ - Initialise the fusb302 to a host - """ - # put device into a known state - self.reset() - self.power_up() - # set power and data roles - self.i2c.writeto_mem(self.ADDRESS, 0x03, bytes([0xB0])) - # setup pull ups and measure on cc1 - self.i2c.writeto_mem(self.ADDRESS, 0x02, bytes([0xC0])) - self.cc_select = 1 - # set measurement level - self.i2c.writeto_mem(self.ADDRESS, 0x04, bytes([0x25])) - # setup 3 auto retries for PD - self.i2c.writeto_mem(self.ADDRESS, 0x09, bytes([0x07])) - # unmask BC level, activity, OCP and good CRC sent interrupts - self.i2c.writeto_mem(self.ADDRESS, 0x0A, bytes([0xBE])) - self.i2c.writeto_mem(self.ADDRESS, 0x0E, bytes([0x7F])) - self.i2c.writeto_mem(self.ADDRESS, 0x0F, bytes([0x00])) - # flush buffers enable interrupts and set host current to the 1.5A current limit - self.i2c.writeto_mem(self.ADDRESS, 0x06, bytes([0x48])) - self.i2c.writeto_mem(self.ADDRESS, 0x07, bytes([0x04])) - self.setup_pd() - self.host = True - - def setup_pd(self): - """ - get the bmc ready for comms - """ - self.write_bits(self.rx_flush, 1) - if self.cc_select == 1: - self.write_bits(self.enable_bmc_cc1, 1) - elif self.cc_select == 2: - self.write_bits(self.enable_bmc_cc2, 1) - self.write_bits(self.tx_flush, 1) - self.write_bits(self.rx_flush, 1) - self.reset_PD() - - # FIFO tokens - TX_ON = const(0xA1) - TX_SOP1 = const(0x12) - TX_SOP2 = const(0x13) - TX_SOP3 = const(0x1B) - TX_RESET1 = const(0x15) - TX_RESET2 = const(0x16) - TX_PACKSYM = const(0x80) - TX_JAM_CRC = const(0xFF) - TX_EOP = const(0x14) - TX_OFF = const(0xFE) - RX_SOP = const(0xE0) - RX_SOP1 = const(0xC0) - RX_SOP2 = const(0xA0) - RX_SOP1DB = const(0x80) - RX_SOP2DB = const(0x60) - - """ - following taken from - https://github.com/CRImier/HaD_talking_pd/blob/main/main.py - with a little modification - """ - - def get_rxb(self, length=80): - # read the FIFO contents - return self.i2c.readfrom_mem(self.ADDRESS, 0x43, length) - - def request_pdo(self, num, current, max_current, msg_id=0): - sop_seq = [ - self.TX_SOP1, - self.TX_SOP1, - self.TX_SOP1, - self.TX_SOP2, - self.TX_PACKSYM, - ] - eop_seq = [self.TX_JAM_CRC, self.TX_EOP, self.TX_OFF, self.TX_ON] - obj_count = 1 - pdo_len = 2 + (4 * obj_count) - pdo = [0 for i in range(pdo_len)] - - pdo[0] |= 0b10 << 6 # PD 3.0 - pdo[0] |= 0b00010 # request - - pdo[1] |= obj_count << 4 - pdo[1] |= (msg_id & 0b111) << 1 # message ID - - # packing max current into fields - max_current_b = max_current // 10 - max_current_l = max_current_b & 0xFF - max_current_h = max_current_b >> 8 - pdo[2] = max_current_l - pdo[3] |= max_current_h - - # packing current into fields - current_b = current // 10 - current_l = current_b & 0x3F - current_h = current_b >> 6 - pdo[3] |= current_l << 2 - pdo[4] |= current_h - - pdo[5] |= (num + 1) << 4 # object position - pdo[5] |= 0b1 # no suspend - - sop_seq[4] |= pdo_len - - self.i2c.writeto_mem(self.ADDRESS, self.rxtx_fifo.register, bytes(sop_seq)) - self.i2c.writeto_mem(self.ADDRESS, self.rxtx_fifo.register, bytes(pdo)) - self.i2c.writeto_mem(self.ADDRESS, self.rxtx_fifo.register, bytes(eop_seq)) - - pdo_types = ["fixed", "batt", "var", "pps"] - pps_types = ["spr", "epr", "res", "res"] - - def read_pdos(self): - pdo_list = [] - header = self.get_rxb(1)[0] - if header == 0xE0: - b1, b0 = self.get_rxb(2) - pdo_count = (b0 >> 4) & 0b111 - read_len = pdo_count * 4 - pdos = self.get_rxb(read_len) - _ = self.get_rxb(4) # crc - for pdo_i in range(pdo_count): - pdo_bytes = pdos[(pdo_i * 4) :][:4] - parsed_pdo = self.parse_pdo(pdo_bytes) - pdo_list.append(parsed_pdo) - return pdo_list - - def select_pdo(self, pdos): - """ """ - for i, pdo in enumerate(pdos): - if pdo[0] != "fixed": # skipping variable PDOs for now - pass - voltage = pdo[1] - if voltage == 9000: - return (i, 500) - - def parse_pdo(self, pdo): - pdo_t = self.pdo_types[pdo[3] >> 6] - if pdo_t == "fixed": - current_h = pdo[1] & 0b11 - current_b = (current_h << 8) | pdo[0] - current = current_b * 10 - voltage_h = pdo[2] & 0b1111 - voltage_b = (voltage_h << 6) | (pdo[1] >> 2) - voltage = voltage_b * 50 - peak_current = (pdo[2] >> 4) & 0b11 - return (pdo_t, voltage, current, peak_current, pdo[3]) - elif pdo_t in ["batt", "var"]: - # TODO am not motivated to parse these - return (pdo_t, pdo) - elif pdo_t == "pps": - t = (pdo[3] >> 4) & 0b11 - limited = (pdo[3] >> 5) & 0b1 - max_voltage_h = pdo[3] & 0b1 - max_voltage_b = (max_voltage_h << 7) | pdo[2] >> 1 - max_voltage = max_voltage_b * 100 - min_voltage = pdo[1] * 100 - max_current_b = pdo[0] & 0b1111111 - max_current = max_current_b * 50 - return ( - "pps", - self.pps_types[t], - max_voltage, - min_voltage, - max_current, - limited, - ) - - def request_capability(self, msg_id=0): - """ - ask for the power supply options - """ - seq = [ - self.TX_SOP1, - self.TX_SOP1, - self.TX_SOP1, - self.TX_SOP2, - self.TX_PACKSYM | 0x02, - 0x47, - (0x00 | ((msg_id & 0x07) << 1)), - self.TX_JAM_CRC, - self.TX_EOP, - self.TX_OFF, - self.TX_ON, - ] - - self.i2c.writeto_mem(self.ADDRESS, self.rxtx_fifo.register, bytes(seq)) - - def soft_reset(self): - """ - reset the protocol layer on other port - """ - self.i2c.writeto_mem( - self.ADDRESS, self.rxtx_fifo.register, bytes(self.TX_RESET1) - ) - - -if __name__ == "__main__": - from machine import Pin, I2C - - i2c = I2C(0) - pin_reset_i2c = Pin(9, Pin.OUT) - pin_reset_i2c.on() - usb_in = fusb302(i2c) - usb_in.setup_device() - usb_in.determine_input_current_limit() - print("input current limit: " + str(usb_in.get_input_current_limit()) + "mA") diff --git a/modules/main.py b/modules/main.py index 84520c4..0f77f04 100644 --- a/modules/main.py +++ b/modules/main.py @@ -8,6 +8,7 @@ from system.notification.app import NotificationService from system.launcher.app import Launcher from system.power.handler import PowerEventHandler +from system.power.app import PowerManager from frontboards.twentyfour import TwentyTwentyFour @@ -27,6 +28,9 @@ # Start notification handler scheduler.start_app(NotificationService(), always_on_top=True) +# Start power management app +scheduler.start_app(PowerManager()) + try: wifi.connect() except Exception: diff --git a/modules/system/power/__init__.py b/modules/system/power/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/modules/system/power/app.py b/modules/system/power/app.py new file mode 100644 index 0000000..ef0d57a --- /dev/null +++ b/modules/system/power/app.py @@ -0,0 +1,17 @@ +from app import App +import power +import asyncio + + +class PowerManager(App): + def __init__(self): ... + + + async def background_task(self): + while True: + if power.Vbat() < 3.5 and power.Vin() < 4.5: + power.Off() + await asyncio.sleep(10) + + +__app_export__ = PowerManager