Skip to content
Draft
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
110 changes: 110 additions & 0 deletions cflib2/_rust.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,10 @@ class Crazyflie:
r"""
Get the platform subsystem
"""
def supervisor(self) -> Supervisor:
r"""
Get the supervisor subsystem
"""
def __str__(self) -> builtins.str: ...
def __repr__(self) -> builtins.str: ...

Expand Down Expand Up @@ -423,6 +427,8 @@ class EmergencyControl:

Immediately stops all motors and puts the Crazyflie into a locked state.
The drone will require a reboot before it can fly again.

Deprecated: Use `crazyflie.supervisor().send_emergency_stop()` instead.
"""
async def send_emergency_stop_watchdog(self) -> None:
r"""
Expand All @@ -432,6 +438,8 @@ class EmergencyControl:
the drone if this message isn't sent every 1000ms. Once activated by the first
call, you must continue sending this periodically forever or the drone will
automatically emergency stop. Use only if you need automatic failsafe behavior.

Deprecated: Use `crazyflie.supervisor().send_emergency_stop_watchdog()` instead.
"""

@typing.final
Expand Down Expand Up @@ -1469,6 +1477,8 @@ class Platform:
Arms or disarms the Crazyflie's safety systems. When disarmed, the motors
will not spin even if thrust commands are sent.

Deprecated: Use `crazyflie.supervisor().send_arming_request()` instead.

# Arguments
* `do_arm` - true to arm, false to disarm
"""
Expand All @@ -1477,6 +1487,8 @@ class Platform:
Send crash recovery request

Requests recovery from a crash state detected by the Crazyflie.

Deprecated: Use `crazyflie.supervisor().send_crash_recovery_request()` instead.
"""
async def get_app_channel(self) -> typing.Optional[AppChannel]:
r"""
Expand Down Expand Up @@ -1560,6 +1572,104 @@ class ProtocolVersionNotSupportedError(CrazyflieError):

...

@typing.final
class Supervisor:
r"""
Supervisor subsystem

Monitors the Crazyflie's system state and exposes arming, crash recovery,
and emergency stop controls. Obtain via `crazyflie.supervisor()`.
"""
async def read_bitfield(self) -> builtins.int:
r"""
Read the raw supervisor state bitfield

Returns the raw bitfield as an integer. Uses time-based caching
(100 ms) to avoid flooding the link.
"""
async def active_states(self) -> builtins.list[builtins.str]:
r"""
Names of all currently active states
"""
async def send_arming_request(self, do_arm: builtins.bool) -> None:
r"""
Send system arm/disarm request

Arms or disarms the Crazyflie's motors. When disarmed, the motors
will not spin even if thrust commands are sent.

Args:
do_arm: True to arm, False to disarm
"""
async def send_crash_recovery_request(self) -> None:
r"""
Send crash recovery request

Requests recovery from a crashed state. The firmware may allow
recovery without a full reboot depending on the crash type.
"""
async def send_emergency_stop(self) -> None:
r"""
Send emergency stop

Immediately stops all motors and puts the Crazyflie into a locked state.
The drone will require a reboot before it can fly again.
"""
async def send_emergency_stop_watchdog(self) -> None:
r"""
Send emergency stop watchdog

Activates/resets a watchdog failsafe that will automatically emergency
stop the drone if this message is not sent every 1000 ms. Once
activated, you must keep sending this periodically or the drone will
stop. Use only when you need automatic failsafe behaviour on
communication loss.
"""
async def can_be_armed(self) -> builtins.bool:
r"""
System can be armed - will accept an arming command
"""
async def is_armed(self) -> builtins.bool:
r"""
System is armed
"""
async def is_auto_armed(self) -> builtins.bool:
r"""
System is configured to automatically arm
"""
async def can_fly(self) -> builtins.bool:
r"""
The Crazyflie is ready to fly
"""
async def is_flying(self) -> builtins.bool:
r"""
The Crazyflie is flying
"""
async def is_tumbled(self) -> builtins.bool:
r"""
The Crazyflie is tumbled (upside down)
"""
async def is_locked(self) -> builtins.bool:
r"""
The Crazyflie is in the locked state and must be restarted
"""
async def is_crashed(self) -> builtins.bool:
r"""
The Crazyflie has crashed
"""
async def hl_control_active(self) -> builtins.bool:
r"""
High level commander is actively flying the drone
"""
async def hl_traj_finished(self) -> builtins.bool:
r"""
High level commander trajectory has finished
"""
async def hl_control_disabled(self) -> builtins.bool:
r"""
High level commander is disabled and not producing setpoints
"""

class SystemError(CrazyflieError):
r"""
Async executor error.
Expand Down
27 changes: 27 additions & 0 deletions cflib2/supervisor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
#
# ,---------, ____ _ __
# | ,-^-, | / __ )(_) /_______________ _____ ___
# | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \
# | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
# +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/
#
# Copyright (C) 2025 Bitcraze AB
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Supervisor subsystem types"""

from cflib2._rust import Supervisor

__all__ = ["Supervisor"]
8 changes: 4 additions & 4 deletions examples/arming.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async def main() -> None:
cf = await Crazyflie.connect_from_uri(context, args.uri)
print("Connected!")

platform = cf.platform()
supervisor = cf.supervisor()

try:
print("\n⚠️ WARNING: This will ARM the Crazyflie!")
Expand All @@ -64,7 +64,7 @@ async def main() -> None:

# Arm the Crazyflie
print("\n1. Arming the Crazyflie...")
await platform.send_arming_request(do_arm=True)
await supervisor.send_arming_request(do_arm=True)
print(" ✓ Armed! Motors can now spin.")

# Wait a few seconds
Expand All @@ -76,14 +76,14 @@ async def main() -> None:

# Disarm the Crazyflie
print("\n3. Disarming the Crazyflie...")
await platform.send_arming_request(do_arm=False)
await supervisor.send_arming_request(do_arm=False)
print(" ✓ Disarmed! Motors are now disabled.")

print("\n✓ Arming cycle complete!")

except KeyboardInterrupt:
print("\n\n⚠️ Interrupted! Disarming for safety...")
await platform.send_arming_request(do_arm=False)
await supervisor.send_arming_request(do_arm=False)
print(" ✓ Disarmed!")

finally:
Expand Down
10 changes: 4 additions & 6 deletions examples/emergency_stop.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,8 @@ async def main() -> None:
cf = await Crazyflie.connect_from_uri(context, args.uri)
print("Connected!")

platform = cf.platform()
supervisor = cf.supervisor()
commander = cf.commander()
localization = cf.localization()
emergency = localization.emergency()

try:
print("\n⚠️ WARNING: This will ARM and SPIN the motors!")
Expand All @@ -69,7 +67,7 @@ async def main() -> None:

# Arm the Crazyflie
print("\n1. Arming the Crazyflie...")
await platform.send_arming_request(do_arm=True)
await supervisor.send_arming_request(do_arm=True)
await asyncio.sleep(0.3)
print(" ✓ Armed!")

Expand All @@ -91,7 +89,7 @@ async def main() -> None:

# Send emergency stop
print("\n4. Sending emergency stop command...")
await emergency.send_emergency_stop()
await supervisor.send_emergency_stop()
await asyncio.sleep(0.5)
print(" ✓ Emergency stop sent!")

Expand All @@ -101,7 +99,7 @@ async def main() -> None:
except KeyboardInterrupt:
print("\n\n⚠️ Interrupted! Attempting to disarm for safety...")
try:
await platform.send_arming_request(do_arm=False)
await supervisor.send_arming_request(do_arm=False)
print(" ✓ Disarmed!")
except Exception:
print(" ⚠️ Could not disarm (may already be in emergency state)")
Expand Down
12 changes: 5 additions & 7 deletions examples/emergency_watchdog.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,8 @@ async def main() -> None:
cf = await Crazyflie.connect_from_uri(context, args.uri)
print("Connected!")

platform = cf.platform()
supervisor = cf.supervisor()
commander = cf.commander()
localization = cf.localization()
emergency = localization.emergency()

try:
print("\n⚠️ WARNING: This will ARM and SPIN the motors!")
Expand All @@ -75,7 +73,7 @@ async def main() -> None:

# Arm the Crazyflie
print("\n1. Arming the Crazyflie...")
await platform.send_arming_request(do_arm=True)
await supervisor.send_arming_request(do_arm=True)
await asyncio.sleep(0.3)
print(" ✓ Armed!")

Expand All @@ -96,7 +94,7 @@ async def main() -> None:

# Activate watchdog
print("\n4. Activating watchdog (1000ms timeout)...")
await emergency.send_emergency_stop_watchdog()
await supervisor.send_emergency_stop_watchdog()
print(" ✓ Watchdog activated!")

print(
Expand All @@ -111,7 +109,7 @@ async def main() -> None:
sys.stdout.flush()

# Send watchdog message
await emergency.send_emergency_stop_watchdog()
await supervisor.send_emergency_stop_watchdog()

# Keep motors spinning and wait 800ms
for _ in range(8):
Expand Down Expand Up @@ -143,7 +141,7 @@ async def main() -> None:
except KeyboardInterrupt:
print("\n\n⚠️ Interrupted! Attempting to disarm for safety...")
try:
await platform.send_arming_request(do_arm=False)
await supervisor.send_arming_request(do_arm=False)
print(" ✓ Disarmed!")
except Exception:
print(" ⚠️ Could not disarm (may already be in emergency state)")
Expand Down
Loading
Loading