diff --git a/Batt_Board/lib/pysquared_eps.py b/Batt_Board/lib/pysquared_eps.py index cbd79ae6..846152b9 100644 --- a/Batt_Board/lib/pysquared_eps.py +++ b/Batt_Board/lib/pysquared_eps.py @@ -20,7 +20,7 @@ import adafruit_tca9548a # I2C Multiplexer import adafruit_pct2075 # Temperature Sensor import adafruit_max31856 # Thermocouple -import adafruit_vl6180x # LiDAR Distance Sensor for Antenna +import adafruit_vl6180x # LiDAR Distance Sensor for Antenna import adafruit_ina219 # Power Monitor # CAN Bus Import @@ -183,7 +183,6 @@ def __init__(self): "ERROR INITIALIZING FACES: " + "".join(traceback.format_exception(e)) ) - if self.c_boot > 200: self.c_boot = 0 @@ -230,13 +229,13 @@ def __init__(self): self.debug_print( "[ERROR][SOLAR Power Monitor]" + "".join(traceback.format_exception(e)) ) - + # Initialize LiDAR try: - self.LiDAR = adafruit_vl6180x.VL6180X(self.i2c1,offset=0) + self.LiDAR = adafruit_vl6180x.VL6180X(self.i2c1, offset=0) self.hardware["LiDAR"] = True except Exception as e: - self.debug_print('[ERROR][LiDAR]' + ''.join(traceback.format_exception(e))) + self.debug_print("[ERROR][LiDAR]" + "".join(traceback.format_exception(e))) # Define Charge Indicate Pin self.charge_indicate = digitalio.DigitalInOut(board.CHRG) @@ -281,7 +280,7 @@ def __init__(self): try: self.spi0cs0 = digitalio.DigitalInOut(board.SPI0_CS0) self.spi0cs0.switch_to_output() - self.can_bus = CAN(self.spi0, self.spi0cs2, loopback=True, silent=True) + self.can_bus = CAN(self.spi0, self.spi0cs0, loopback=True, silent=True) self.hardware["CAN"] = True except Exception as e: @@ -633,12 +632,10 @@ def heater_on(self): self.heater.duty_cycle = 0x7FFF except Exception as e: self.debug_print( - "[ERROR] Cant turn on heater: " - + "".join(traceback.format_exception(e)) + "[ERROR] Cant turn on heater: " + "".join(traceback.format_exception(e)) ) self.heater.duty_cycle = 0x0000 - def heater_off(self): if self.hardware["FLD"]: try: @@ -664,18 +661,20 @@ def heater_off(self): # =======================================================# def distance(self): - if self.hardware['LiDAR']: + if self.hardware["LiDAR"]: try: distance_mm = 0 for _ in range(10): distance_mm += self.LiDAR.range time.sleep(0.01) - self.debug_print('distance measured = {0}mm'.format(distance_mm/10)) - return distance_mm/10 + self.debug_print("distance measured = {0}mm".format(distance_mm / 10)) + return distance_mm / 10 except Exception as e: - self.debug_print('LiDAR error: ' + ''.join(traceback.format_exception(e))) + self.debug_print( + "LiDAR error: " + "".join(traceback.format_exception(e)) + ) else: - self.debug_print('[WARNING] LiDAR not initialized') + self.debug_print("[WARNING] LiDAR not initialized") return 0 def burn(self, burn_num, dutycycle=0, freq=1000, duration=1): @@ -735,7 +734,7 @@ def burn(self, burn_num, dutycycle=0, freq=1000, duration=1): burnwire.deinit() self._relayA.drive_mode = digitalio.DriveMode.OPEN_DRAIN - def smart_burn(self,burn_num,dutycycle=0.1): + def smart_burn(self, burn_num, dutycycle=0.1): """ Operate burn wire circuits. Wont do anything unless the a nichrome burn wire has been installed. @@ -751,107 +750,119 @@ def smart_burn(self,burn_num,dutycycle=0.1): freq = 1000 - distance1=0 - distance2=0 - #self.dist=self.distance() + distance1 = 0 + distance2 = 0 + # self.dist=self.distance() try: # convert duty cycle % into 16-bit fractional up time - dtycycl=int((dutycycle/100)*(0xFFFF)) - self.debug_print('----- SMART BURN WIRE CONFIGURATION -----') - self.debug_print('\tFrequency of: {}Hz\n\tDuty cycle of: {}% (int:{})'.format(freq,(100*dtycycl/0xFFFF),dtycycl)) + dtycycl = int((dutycycle / 100) * (0xFFFF)) + self.debug_print("----- SMART BURN WIRE CONFIGURATION -----") + self.debug_print( + "\tFrequency of: {}Hz\n\tDuty cycle of: {}% (int:{})".format( + freq, (100 * dtycycl / 0xFFFF), dtycycl + ) + ) # create our PWM object for the respective pin # not active since duty_cycle is set to 0 (for now) - if '1' in burn_num: + if "1" in burn_num: burnwire = pwmio.PWMOut(board.ENABLE_BURN, frequency=freq, duty_cycle=0) else: return False - try: - distance1=self.distance() + distance1 = self.distance() self.debug_print(str(distance1)) - if distance1 > self.dist+2 and distance1 > 4 or self.f_triedburn == True: + if ( + distance1 > self.dist + 2 + and distance1 > 4 + or self.f_triedburn == True + ): self.burned = True self.f_brownout = True - raise TypeError("Wire seems to have burned and satellite browned out") + raise TypeError( + "Wire seems to have burned and satellite browned out" + ) else: - self.dist=int(distance1) - self.burnarm=True + self.dist = int(distance1) + self.burnarm = True if self.burnarm: - self.burnarm=False + self.burnarm = False self.f_triedburn = True # Configure the relay control pin & open relay - self.RGB=(0,165,0) + self.RGB = (0, 165, 0) - self._relayA.drive_mode=digitalio.DriveMode.PUSH_PULL - self.RGB=(255,165,0) + self._relayA.drive_mode = digitalio.DriveMode.PUSH_PULL + self.RGB = (255, 165, 0) self._relayA.value = 1 # Pause to ensure relay is open time.sleep(0.5) - #Start the Burn - burnwire.duty_cycle=dtycycl + # Start the Burn + burnwire.duty_cycle = dtycycl - #Burn Timer + # Burn Timer start_time = time.monotonic() - #Monitor the burn + # Monitor the burn while not self.burned: - distance2=self.distance() + distance2 = self.distance() self.debug_print(str(distance2)) - if distance2 > distance1+1 or distance2 > 10: + if distance2 > distance1 + 1 or distance2 > 10: self._relayA.value = 0 burnwire.duty_cycle = 0 - self.burned=True + self.burned = True self.f_triedburn = False else: - distance1=distance2 + distance1 = distance2 time_elapsed = time.monotonic() - start_time print("Time Elapsed: " + str(time_elapsed)) if time_elapsed > 4: self._relayA.value = 0 burnwire.duty_cycle = 0 - self.burned=False - self.RGB=(0,0,255) + self.burned = False + self.RGB = (0, 0, 255) time.sleep(10) self.f_triedburn = False break time.sleep(5) - distance2=self.distance() + distance2 = self.distance() else: pass - if distance2 > distance1+2 or distance2 > 10: - self.burned=True + if distance2 > distance1 + 2 or distance2 > 10: + self.burned = True self.f_triedburn = False except Exception as e: - self.debug_print("Error in Burn Sequence: " + ''.join(traceback.format_exception(e))) + self.debug_print( + "Error in Burn Sequence: " + "".join(traceback.format_exception(e)) + ) self.debug_print("Error: " + str(e)) if "no attribute 'LiDAR'" in str(e): self.debug_print("Burning without LiDAR") - time.sleep(120) #Set to 120 for flight - self.burnarm=False - self.burned=True - self.f_triedburn=True - self.burn("1",dutycycle,freq,4) + time.sleep(120) # Set to 120 for flight + self.burnarm = False + self.burned = True + self.f_triedburn = True + self.burn("1", dutycycle, freq, 4) time.sleep(5) finally: self._relayA.value = 0 - burnwire.duty_cycle=0 - self.RGB=(0,0,0) + burnwire.duty_cycle = 0 + self.RGB = (0, 0, 0) burnwire.deinit() - self._relayA.drive_mode=digitalio.DriveMode.OPEN_DRAIN + self._relayA.drive_mode = digitalio.DriveMode.OPEN_DRAIN return True except Exception as e: - self.debug_print("Error with Burn Wire: " + ''.join(traceback.format_exception(e))) + self.debug_print( + "Error with Burn Wire: " + "".join(traceback.format_exception(e)) + ) return False - print("Initializing Power Management Systems...") cubesat = Satellite() diff --git a/FC_Board/code.py b/FC_Board/code.py index 4eba7a01..6e67537f 100644 --- a/FC_Board/code.py +++ b/FC_Board/code.py @@ -4,21 +4,23 @@ """ Built for the PySquared FC Board -Version: 1.0.1 (Beta) -Published: July 26, 2024 +Version: 1.1.1 (Beta) +Published: October 15, 2024 """ import time print("=" * 70) print("Hello World!") -print("PySquared FC Board Circuit Python Software Version: 1.0.1 (Beta)") -print("Published: July 26, 2024") +print("PySquared FC Board Circuit Python Software Version: 1.1.1 (Beta)") +print("Published: October 15, 2024") print("=" * 70) +loiter_time = 5 + try: - for i in range(10): - print(f"Code Starting in {10-i} seconds") + for i in range(loiter_time): + print(f"Code Starting in {loiter_time-i} seconds") time.sleep(1) import main diff --git a/FC_Board/lib/pysquared.py b/FC_Board/lib/pysquared.py index bc964288..6af79d24 100755 --- a/FC_Board/lib/pysquared.py +++ b/FC_Board/lib/pysquared.py @@ -1,7 +1,7 @@ """ CircuitPython driver for PySquared satellite board. -PySquared Hardware Version: mainboard-v01 -CircuitPython Version: 8.0.0 alpha +PySquared Hardware Version: Flight Controller V4c +CircuitPython Version: 9.0.0 Library Repo: * Author(s): Nicole Maggard, Michael Pham, and Rachel Sarmiento @@ -24,6 +24,7 @@ from adafruit_lsm6ds.lsm6dsox import LSM6DSOX # IMU import adafruit_lis2mdl # Magnetometer import adafruit_tca9548a # I2C Multiplexer +import rv3028 # CAN Bus Import from adafruit_mcp2515 import MCP2515 as CAN @@ -64,6 +65,9 @@ class Satellite: f_fsk = bitFlag(register=_FLAG, bit=7) def debug_print(self, statement): + """ + A method for printing debug statements. This method will only print if the self.debug flag is set to True. + """ if self.debug: print(co("[pysquared]" + str(statement), "green", "bold")) @@ -80,14 +84,6 @@ def __init__(self): self.heating = False # Currently not used self.is_licensed = False - """ - Define the boot time and current time - """ - self.BOOTTIME = 1577836800 - self.debug_print(f"Boot time: {self.BOOTTIME}s") - self.CURRENTTIME = self.BOOTTIME - self.UPTIME = 0 - """ Define the normal power modes """ @@ -111,6 +107,15 @@ def __init__(self): self.send_buff = memoryview(SEND_BUFF) self.micro = microcontroller + """ + Define the boot time and current time + """ + self.c_boot += 1 + self.BOOTTIME = 1577836800 + self.debug_print(f"Boot time: {self.BOOTTIME}s") + self.CURRENTTIME = self.BOOTTIME + self.UPTIME = 0 + self.radio_cfg = { "id": 0xFB, "gs": 0xFA, @@ -134,6 +139,7 @@ def __init__(self): "WDT": False, "TCA": False, "CAN": False, + "RTC": False, "Face0": False, "Face1": False, "Face2": False, @@ -154,6 +160,14 @@ def __init__(self): if self.f_softboot: self.f_softboot = False + """ + Setting up the watchdog pin. + """ + + self.watchdog_pin = digitalio.DigitalInOut(board.WDT_WDI) + self.watchdog_pin.direction = digitalio.Direction.OUTPUT + self.watchdog_pin.value = False + """ Intializing Communication Buses """ @@ -283,6 +297,22 @@ def __init__(self): "[ERROR][CAN TRANSCEIVER]" + "".join(traceback.format_exception(e)) ) + """ + RTC Initialization + """ + try: + self.rtc = rv3028(self.i2c1) + + # Still need to test these configs + self.rtc.configure_backup_switchover(mode="level", interrupt=True) + self.rtc.configure_evi(enable=True, timestamp_mode="last") + self.hardware["RTC"] = True + + except Exception as e: + self.debug_print( + "[ERROR][Real Time Clock]" + "".join(traceback.format_exception(e)) + ) + """ SD Card Initialization """ @@ -488,6 +518,94 @@ def mag(self): except Exception as e: self.error_print("[ERROR][mag]" + "".join(traceback.format_exception(e))) + @property + def time(self): + try: + return self.rtc.get_time() + except Exception as e: + self.error_print("[ERROR][RTC]" + "".join(traceback.format_exception(e))) + + @time.setter + def time(self, hours, minutes, seconds): + if self.hardware["RTC"]: + try: + self.rtc.set_time(hours, minutes, seconds) + except Exception as e: + self.error_print( + "[ERROR][RTC]" + "".join(traceback.format_exception(e)) + ) + else: + self.error_print("[WARNING] RTC not initialized") + + @property + def date(self): + try: + return self.rtc.get_date() + except Exception as e: + self.error_print("[ERROR][RTC]" + "".join(traceback.format_exception(e))) + + @time.setter + def date(self, year, month, date, weekday): + if self.hardware["RTC"]: + try: + self.rtc.set_date(year, month, date, weekday) + except Exception as e: + self.error_print( + "[ERROR][RTC]" + "".join(traceback.format_exception(e)) + ) + else: + self.error_print("[WARNING] RTC not initialized") + + """ + Maintenence Functions + """ + + def watchdog_pet(self): + self.watchdog_pin.value = True + time.sleep(0.1) + self.watchdog_pin.value = False + + def check_reboot(self): + self.UPTIME = self.uptime + self.debug_print(str("Current up time: " + str(self.UPTIME))) + if self.UPTIME > 86400: + self.micro.reset() + + def powermode(self, mode): + """ + Configure the hardware for minimum or normal power consumption + Add custom modes for mission-specific control + """ + try: + if "crit" in mode: + self.neopixel.brightness = 0 + self.enable_rf.value = False + self.power_mode = "critical" + + elif "min" in mode: + self.neopixel.brightness = 0 + self.enable_rf.value = False + + self.power_mode = "minimum" + + elif "norm" in mode: + self.enable_rf.value = True + self.power_mode = "normal" + # don't forget to reconfigure radios, gps, etc... + + elif "max" in mode: + self.enable_rf.value = True + self.power_mode = "maximum" + except Exception as e: + self.error_print( + "Error in changing operations of powermode: " + + "".join(traceback.format_exception(e)) + ) + + """ + SD Card Functions + """ + def log(self, filedir, msg): if self.hardware["SDcard"]: try: @@ -502,12 +620,6 @@ def log(self, filedir, msg): else: self.error_print("[WARNING] SD Card not initialized") - def check_reboot(self): - self.UPTIME = self.uptime - self.debug_print(str("Current up time: " + str(self.UPTIME))) - if self.UPTIME > 86400: - self.micro.reset() - def print_file(self, filedir=None, binary=False): try: if filedir == None: @@ -546,37 +658,6 @@ def read_file(self, filedir=None, binary=False): "[ERROR] Cant print file: " + "".join(traceback.format_exception(e)) ) - def powermode(self, mode): - """ - Configure the hardware for minimum or normal power consumption - Add custom modes for mission-specific control - """ - try: - if "crit" in mode: - self.neopixel.brightness = 0 - self.enable_rf.value = False - self.power_mode = "critical" - - elif "min" in mode: - self.neopixel.brightness = 0 - self.enable_rf.value = False - - self.power_mode = "minimum" - - elif "norm" in mode: - self.enable_rf.value = True - self.power_mode = "normal" - # don't forget to reconfigure radios, gps, etc... - - elif "max" in mode: - self.enable_rf.value = True - self.power_mode = "maximum" - except Exception as e: - self.error_print( - "Error in changing operations of powermode: " - + "".join(traceback.format_exception(e)) - ) - def new_file(self, substring, binary=False): """ substring something like '/data/DATA_' diff --git a/FC_Board/lib/rv3028.py b/FC_Board/lib/rv3028.py new file mode 100644 index 00000000..fb1605ab --- /dev/null +++ b/FC_Board/lib/rv3028.py @@ -0,0 +1,239 @@ +""" +This class handles communications + +Authors: Nicole Maggard, Michael Pham, and Rachel Sarmiento +""" + +import time +import adafruit_bus_device.i2c_device as i2c_device + + +class RV3028: + # Register addresses + SECONDS = 0x00 + MINUTES = 0x01 + HOURS = 0x02 + WEEKDAY = 0x03 + DATE = 0x04 + MONTH = 0x05 + YEAR = 0x06 + STATUS = 0x0E + CONTROL1 = 0x0F + CONTROL2 = 0x10 + EVENT_CONTROL = 0x13 + TIMESTAMP_COUNT = 0x14 + TIMESTAMP_SECONDS = 0x15 + TIMESTAMP_MINUTES = 0x16 + TIMESTAMP_HOURS = 0x17 + TIMESTAMP_DATE = 0x18 + TIMESTAMP_MONTH = 0x19 + TIMESTAMP_YEAR = 0x1A + EEPROM_BACKUP = 0x37 + + def __init__(self, i2c_bus, address=0x52): + self.i2c_device = i2c_device.I2CDevice(i2c_bus, address) + + def _read_register(self, register, length=1): + with self.i2c_device as i2c: + i2c.write(bytes([register])) + result = bytearray(length) + i2c.readinto(result) + return result + + def _write_register(self, register, data): + with self.i2c_device as i2c: + i2c.write(bytes([register]) + data) + + def _bcd_to_int(self, bcd): + return (bcd & 0x0F) + ((bcd >> 4) * 10) + + def _int_to_bcd(self, value): + return ((value // 10) << 4) | (value % 10) + + def set_time(self, hours, minutes, seconds): + data = bytes( + [ + self._int_to_bcd(seconds), + self._int_to_bcd(minutes), + self._int_to_bcd(hours), + ] + ) + self._write_register(self.SECONDS, data) + + def get_time(self): + data = self._read_register(self.SECONDS, 3) + return ( + self._bcd_to_int(data[2]), # hours + self._bcd_to_int(data[1]), # minutes + self._bcd_to_int(data[0]), # seconds + ) + + def set_date(self, year, month, date, weekday): + data = bytes( + [ + self._int_to_bcd(weekday), + self._int_to_bcd(date), + self._int_to_bcd(month), + self._int_to_bcd(year), + ] + ) + self._write_register(self.WEEKDAY, data) + + def get_date(self): + data = self._read_register(self.WEEKDAY, 4) + return ( + self._bcd_to_int(data[3]), # year + self._bcd_to_int(data[2]), # month + self._bcd_to_int(data[1]), # date + self._bcd_to_int(data[0]), # weekday + ) + + def set_alarm(self, minute, hour, weekday): + # Set alarm mask to check for minute, hour, and weekday match + control2 = self._read_register(self.CONTROL2)[0] + control2 |= 0x08 # Set AIE (Alarm Interrupt Enable) bit + self._write_register(self.CONTROL2, bytes([control2])) + + data = bytes( + [ + self._int_to_bcd(minute), + self._int_to_bcd(hour), + self._int_to_bcd(weekday), + ] + ) + self._write_register(self.MINUTES, data) + + def enable_trickle_charger(self, resistance=3000): + control1 = self._read_register(self.CONTROL1)[0] + control1 |= 0x20 # Set TCE (Trickle Charge Enable) bit + + # Set TCR (Trickle Charge Resistor) bits + if resistance == 3000: + control1 |= 0x00 + elif resistance == 5000: + control1 |= 0x01 + elif resistance == 9000: + control1 |= 0x02 + elif resistance == 15000: + control1 |= 0x03 + else: + raise ValueError("Invalid trickle charger resistance") + + self._write_register(self.CONTROL1, bytes([control1])) + + def disable_trickle_charger(self): + control1 = self._read_register(self.CONTROL1)[0] + control1 &= ~0x20 # Clear TCE (Trickle Charge Enable) bit + self._write_register(self.CONTROL1, bytes([control1])) + + def configure_evi(self, enable=True): + """ + Configure EVI for rising edge detection, enable time stamping, + and enable interrupt. + + :param enable: True to enable EVI, False to disable + """ + if enable: + # Configure Event Control Register + event_control = 0x40 # EHL = 1 (rising edge), ET = 00 (no filtering) + self._write_register(self.EVENT_CONTROL, bytes([event_control])) + + # Enable time stamping and EVI interrupt + control2 = self._read_register(self.CONTROL2)[0] + control2 |= 0x84 # Set TSE (bit 7) and EIE (bit 2) + self._write_register(self.CONTROL2, bytes([control2])) + else: + # Disable time stamping and EVI interrupt + control2 = self._read_register(self.CONTROL2)[0] + control2 &= ~0x84 # Clear TSE (bit 7) and EIE (bit 2) + self._write_register(self.CONTROL2, bytes([control2])) + + def get_event_timestamp(self): + """ + Read the timestamp of the last EVI event. + + :return: Tuple of (year, month, date, hours, minutes, seconds, count) + """ + data = self._read_register(self.TIMESTAMP_COUNT, 7) + return ( + self._bcd_to_int(data[6]), # year + self._bcd_to_int(data[5]), # month + self._bcd_to_int(data[4]), # date + self._bcd_to_int(data[3]), # hours + self._bcd_to_int(data[2]), # minutes + self._bcd_to_int(data[1]), # seconds + data[0], # count (not BCD) + ) + + def clear_event_flag(self): + """ + Clear the Event Flag (EVF) in the Status Register. + """ + status = self._read_register(self.STATUS)[0] + status &= ~0x02 # Clear EVF (bit 1) + self._write_register(self.STATUS, bytes([status])) + + def is_event_flag_set(self): + """ + Check if the Event Flag (EVF) is set in the Status Register. + + :return: True if EVF is set, False otherwise + """ + status = self._read_register(self.STATUS)[0] + return bool(status & 0x02) # Check EVF (bit 1) + + def configure_backup_switchover(self, mode="level", interrupt=False): + """ + Configure the Automatic Backup Switchover function. + + :param mode: 'level' for Level Switching Mode (LSM), + 'direct' for Direct Switching Mode (DSM), + or 'disabled' to disable switchover + :param interrupt: True to enable backup switchover interrupt, False to disable + """ + backup_reg = self._read_register(self.EEPROM_BACKUP)[0] + + # Clear existing BSM bits + backup_reg &= ~0x0C + + if mode == "level": + backup_reg |= 0x0C # Set BSM to 11 for LSM + elif mode == "direct": + backup_reg |= 0x04 # Set BSM to 01 for DSM + elif mode == "disabled": + pass # BSM is already cleared to 00 + else: + raise ValueError("Invalid mode. Use 'level', 'direct', or 'disabled'.") + + # Configure backup switchover interrupt + if interrupt: + backup_reg |= 0x40 # Set BSIE bit + else: + backup_reg &= ~0x40 # Clear BSIE bit + + # Always enable fast edge detection + backup_reg |= 0x10 # Set FEDE bit + + # Write the configuration to EEPROM + self._write_register(self.EEPROM_BACKUP, bytes([backup_reg])) + + # Update EEPROM (command 0x11) + self._write_register(0x27, bytes([0x00])) # First command must be 00h + self._write_register(0x27, bytes([0x11])) # Update command + + def is_backup_switchover_occurred(self): + """ + Check if a backup switchover has occurred. + + :return: True if switchover occurred, False otherwise + """ + status = self._read_register(self.STATUS)[0] + return bool(status & 0x20) # Check BSF (bit 5) + + def clear_backup_switchover_flag(self): + """ + Clear the Backup Switchover Flag (BSF) in the Status Register. + """ + status = self._read_register(self.STATUS)[0] + status &= ~0x20 # Clear BSF (bit 5) + self._write_register(self.STATUS, bytes([status])) diff --git a/FC_Board/main.py b/FC_Board/main.py index 329b3231..be1cbd74 100644 --- a/FC_Board/main.py +++ b/FC_Board/main.py @@ -1,6 +1,7 @@ """ Created by Nicole Maggard and Michael Pham 8/19/2022 Updated for Yearling by Nicole Maggard and Rachel Sarmiento 2/4/2023 +Updated again for Orpheus by Michael Pham 9/30/2024 This is where the processes get scheduled, and satellite operations are handeled """ @@ -21,22 +22,26 @@ def debug_print(statement): f = functions.functions(c) try: - c.c_boot += 1 # Increment boot number debug_print("Boot number: " + str(c.c_boot)) debug_print(str(gc.mem_free()) + " Bytes remaining") + c.watchdog_pet() f.beacon() f.listen() + c.watchdog_pet() f.beacon() f.listen() f.state_of_health() f.listen() + c.watchdog_pet() f.beacon() f.listen() f.state_of_health() f.listen() + c.watchdog_pet() + except Exception as e: debug_print("Error in Boot Sequence: " + "".join(traceback.format_exception(e))) finally: @@ -44,20 +49,24 @@ def debug_print(statement): def critical_power_operations(): + c.watchdog_pet() f.beacon() f.listen() f.state_of_health() f.listen() + c.watchdog_pet() f.Long_Hybernate() def minimum_power_operations(): + c.watchdog_pet() f.beacon() f.listen() f.state_of_health() f.listen() + c.watchdog_pet() f.Short_Hybernate() @@ -92,8 +101,10 @@ async def s_lora_beacon(): while check_power(): f.beacon() f.listen() + c.watchdog_pet() f.state_of_health() f.listen() + c.watchdog_pet() time.sleep(1) # Guard Time await asyncio.sleep(30) @@ -182,6 +193,14 @@ async def joke(): gc.collect() await asyncio.sleep(500) + async def check_watchdog(): + + c.hardware["WDT"] = True + while check_power(): + c.watchdog_pet() + await asyncio.sleep(5) + c.hardware["WDT"] = False + async def main_loop(): # log_face_data_task = asyncio.create_task(l_face_data()) @@ -191,8 +210,9 @@ async def main_loop(): t4 = asyncio.create_task(g_face_data()) t5 = asyncio.create_task(detumble()) t6 = asyncio.create_task(joke()) + t7 = asyncio.create_task(check_watchdog()) - await asyncio.gather(t1, t2, t3, t4, t5, t6) + await asyncio.gather(t1, t2, t3, t4, t5, t6, t7) asyncio.run(main_loop()) @@ -232,3 +252,4 @@ async def main_loop(): debug_print("Going Neutral!") c.RGB = (0, 0, 0) + c.hardware["WDT"] = False