Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "[#108] Update watchdog_pet()" #209

Merged
merged 1 commit into from
Mar 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions lib/pysquared/pysquared.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"""

# Common CircuitPython Libs
import asyncio # Add asyncio import
import sys
import time
from collections import OrderedDict
Expand Down Expand Up @@ -578,38 +577,11 @@ def date(self, ymdw: tuple[int, int, int, int]) -> None:
"""

def watchdog_pet(self) -> None:
"""Pet the watchdog timer"""
self.logger.debug("Petting watchdog")
self.watchdog_pin.value = True
time.sleep(0.01)
self.watchdog_pin.value = False

async def _watchdog_pet_task(self) -> None:
"""Async task to continuously pet the watchdog"""
self.logger.info("Starting watchdog petting background task")
while self.hardware.get("WDT", False):
self.watchdog_pet()
await asyncio.sleep(1.0) # Pet watchdog every second
self.logger.info("Watchdog petting task stopped")

def start_watchdog_background_task(self) -> None:
"""Start the watchdog petting as a true background task using asyncio"""
self._last_watchdog_pet = time.monotonic()
self.hardware["WDT"] = True
# Create and schedule the task but don't wait for it
asyncio.create_task(self._watchdog_pet_task())
self.logger.info("Watchdog background task created")

def check_watchdog(self) -> None:
"""
Legacy method maintained for compatibility
The actual petting is now handled by the background task
"""
pass # No-op as this is now handled by the async task

def check_reboot(self) -> None:
"""Check if system needs to be rebooted"""
# No need to check watchdog anymore as it's handled by the background task
self.UPTIME: int = self.get_system_uptime
self.logger.debug("Current up time stat:", uptime=self.UPTIME)
if self.UPTIME > self.reboot_time:
Expand Down
159 changes: 79 additions & 80 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
Published: Nov 19, 2024
"""

import asyncio
import gc
import time

import digitalio
Expand Down Expand Up @@ -53,10 +53,7 @@
config: Config = Config("config.json")

c = pysquared.Satellite(config, logger, __version__)

# Start the watchdog background task
c.start_watchdog_background_task()

c.watchdog_pet()
sleep_helper = SleepHelper(c, logger)

radio_manager = RFM9xManager(
Expand All @@ -72,107 +69,109 @@

f = functions.functions(c, logger, config, sleep_helper, radio_manager)

async def initial_boot():
def initial_boot():
c.watchdog_pet()
f.beacon()
c.watchdog_pet()
f.listen()
c.watchdog_pet()

async def critical_power_operations():
await initial_boot()
# Convert blocking operation to a non-blocking one using run_in_executor
# Since this is a blocking function that will likely put the device to sleep,
# we accept that this will block the event loop as intended
logger.info("Entering long hibernation (this will block the event loop)")
sleep_helper.long_hibernate()
logger.info("Woke from long hibernation")
try:
c.boot_count.increment()

async def minimum_power_operations():
await initial_boot()
# Same as above - this is a blocking operation by design
logger.info("Entering short hibernation (this will block the event loop)")
sleep_helper.short_hibernate()
logger.info("Woke from short hibernation")
logger.info(
"FC Board Stats",
bytes_remaining=gc.mem_free(),
boot_number=c.boot_count.get(),
)

initial_boot()

except Exception as e:
logger.error("Error in Boot Sequence", e)

async def send_imu_data():
finally:
pass

def send_imu_data():
logger.info("Looking to get imu data...")
IMUData = []
c.watchdog_pet()
logger.info("IMU has baton")
IMUData = f.get_imu_data()
c.watchdog_pet()
f.send(IMUData)

async def main():
def main():
f.beacon()

f.listen_loiter()

f.state_of_health()

f.listen_loiter()

f.all_face_data()
c.watchdog_pet()
f.send_face()

f.listen_loiter()
await send_imu_data()

send_imu_data()

f.listen_loiter()

f.joke()

f.listen_loiter()

def critical_power_operations():
initial_boot()
c.watchdog_pet()

sleep_helper.long_hibernate()

def minimum_power_operations():
initial_boot()
c.watchdog_pet()

sleep_helper.short_hibernate()

######################### MAIN LOOP ##############################
async def main_loop():
"""Async main loop that runs alongside the watchdog task"""
try:
while True:
# L0 automatic tasks no matter the battery level
c.check_reboot() # Now this only checks for reboot conditions

if c.power_mode == "critical":
c.rgb = (0, 0, 0)
await critical_power_operations()

elif c.power_mode == "minimum":
c.rgb = (255, 0, 0)
await minimum_power_operations()

elif c.power_mode == "normal":
c.rgb = (255, 255, 0)
await main()

elif c.power_mode == "maximum":
c.rgb = (0, 255, 0)
await main()

else:
f.listen()

# Small yield to allow other tasks to run
await asyncio.sleep(0.1)

except Exception as e:
logger.critical("Critical in Main Loop", e)
time.sleep(10)
microcontroller.on_next_reset(microcontroller.RunMode.NORMAL)
microcontroller.reset()
finally:
logger.info("Going Neutral!")
c.rgb = (0, 0, 0)
c.hardware["WDT"] = False

# Set up the asyncio event loop
async def run_tasks():
# The watchdog task is already started by c.start_watchdog_background_task()
# Just run the main loop as a task
main_task = asyncio.create_task(main_loop())

try:
# Wait for the main task to complete (it should run forever)
await main_task
except asyncio.CancelledError:
logger.info("Main task was cancelled")
except Exception as e:
logger.critical("Error in run_tasks", e)
raise

# Run the asyncio event loop
try:
asyncio.run(run_tasks())
while True:
# L0 automatic tasks no matter the battery level
c.check_reboot()

if c.power_mode == "critical":
c.rgb = (0, 0, 0)
critical_power_operations()

elif c.power_mode == "minimum":
c.rgb = (255, 0, 0)
minimum_power_operations()

elif c.power_mode == "normal":
c.rgb = (255, 255, 0)
main()

elif c.power_mode == "maximum":
c.rgb = (0, 255, 0)
main()

else:
f.listen()

except Exception as e:
logger.critical("Error in asyncio.run", e)
logger.critical("Critical in Main Loop", e)
time.sleep(10)
microcontroller.on_next_reset(microcontroller.RunMode.NORMAL)
microcontroller.reset()
finally:
logger.info("Going Neutral!")

c.rgb = (0, 0, 0)
c.hardware["WDT"] = False

except Exception as e:
logger.critical("An exception occured within main.py", e)