From 760102979e2b93b434465b845c7a5d1fcd6844c7 Mon Sep 17 00:00:00 2001 From: aris Date: Wed, 19 Nov 2025 16:09:46 +0100 Subject: [PATCH 1/9] Added supervisor subsystem --- cflib/crazyflie/supervisor.py | 218 ++++++++++++++++++++++++++++++++++ cflib/crtp/crtpstack.py | 1 + 2 files changed, 219 insertions(+) create mode 100644 cflib/crazyflie/supervisor.py diff --git a/cflib/crazyflie/supervisor.py b/cflib/crazyflie/supervisor.py new file mode 100644 index 000000000..08d15353d --- /dev/null +++ b/cflib/crazyflie/supervisor.py @@ -0,0 +1,218 @@ +# -*- 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, in version 3. +# +# 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 . +""" +Provides access to the supervisor modue of the Crazyflie platform. +""" +import logging +import time + +from cflib.crazyflie.syncCrazyflie import SyncCrazyflie +from cflib.crtp.crtpstack import CRTPPacket +from cflib.crtp.crtpstack import CRTPPort + +__author__ = 'Bitcraze AB' +__all__ = ['SupervisorState'] + +logger = logging.getLogger(__name__) + +# Bit positions +BIT_CAN_BE_ARMED = 0 +BIT_IS_ARMED = 1 +BIT_IS_AUTO_ARMED = 2 +BIT_CAN_FLY = 3 +BIT_IS_FLYING = 4 +BIT_IS_TUMBLED = 5 +BIT_IS_LOCKED = 6 +BIT_IS_CRASHED = 7 +BIT_HL_CONTROL_ACTIVE = 8 +BIT_HL_TRAJ_FINISHED = 9 +BIT_HL_CONTROL_DISABLED = 10 + +CMD_GET_STATE_BITFIELD = 0x0C +SUPERVISOR_CH_INFO = 0 + + +class SupervisorState: + """ + This class is used to easily get the state of your Crazyflie through + the supervisor. Check the `reading_supervisor.py` example in + examples/supervisor/ to better understand how to use it. + """ + STATES = [ + 'Can be armed', + 'Is armed', + 'Is auto armed', + 'Can fly', + 'Is flying', + 'Is tumbled', + 'Is locked', + 'Is crashed', + 'HL control is active', + 'Finished HL trajectory', + 'HL control is disabled' + ] + + def __init__(self, crazyflie): + if isinstance(crazyflie, SyncCrazyflie): + self._cf = crazyflie.cf + else: + self._cf = crazyflie + + self._cache_timeout = 0.1 # seconds + self._last_fetch_time = 0 + self._bitfield = None + self._cf.add_port_callback(CRTPPort.SUPERVISOR, self._supervisor_callback) + self._response_received = False + + def _supervisor_callback(self, pk: CRTPPacket): + """ + Called when a packet is received. + """ + if len(pk.data) < 1: + return + + cmd = pk.data[0] + if cmd & 0x80: # high bit = response + orig_cmd = cmd & 0x7F + if orig_cmd == CMD_GET_STATE_BITFIELD: + self._bitfield = int.from_bytes(pk.data[1:], byteorder='little') + self._response_received = True + logger.info(f'Supervisor bitfield received: 0x{self._bitfield:04X}') + + def _fetch_bitfield(self, timeout=0.2): + """ + Request the bitfield and wait for response (blocking). + Uses time-based cache to avoid sending packages too frequently. + """ + now = time.time() + + # Return cached value if it's recent enough + if self._bitfield is not None and (now - self._last_fetch_time) < self._cache_timeout: + return self._bitfield + + # Send a new request + self._response_received = False + pk = CRTPPacket() + pk.set_header(CRTPPort.SUPERVISOR, SUPERVISOR_CH_INFO) + pk.data = [CMD_GET_STATE_BITFIELD] + self._cf.send_packet(pk) + + # Wait for response + start_time = now + while not self._response_received: + if time.time() - start_time > timeout: + logger.warning('Timeout waiting for supervisor bitfield response') + return self._bitfield or 0 # still return last known value + time.sleep(0.01) + + # Update timestamp + self._last_fetch_time = time.time() + return self._bitfield or 0 + + def _bit(self, position): + bitfield = self._fetch_bitfield() + return bool((bitfield >> position) & 0x01) + + def read_bitfield(self): + """ + Directly get the full bitfield value. + """ + return self._fetch_bitfield() + + def read_state_list(self): + """ + Reads the bitfield and returns the list of all active states. + """ + bitfield = self.read_bitfield() + list = self.decode_bitfield(bitfield) + return list + + def decode_bitfield(self, value): + """ + Given a bitfield integer `value` and a list of `self.STATES`, + returns the names of all states whose bits are set. + Bit 0 corresponds to states[0], Bit 1 to states[1], etc. + * Bit 0 = Can be armed - the system can be armed and will accept an arming command. + * Bit 1 = Is armed - the system is armed. + * Bit 2 = Is auto armed - the system is configured to automatically arm. + * Bit 3 = Can fly - the Crazyflie is ready to fly. + * Bit 4 = Is flying - the Crazyflie is flying. + * Bit 5 = Is tumbled - the Crazyflie is up side down. + * Bit 6 = Is locked - the Crazyflie is in the locked state and must be restarted. + * Bit 7 = Is crashed - the Crazyflie has crashed. + * Bit 8 = High level control is actively flying the drone. + * Bit 9 = High level trajectory has finished. + * Bit 10 = High level control is disabled and not producing setpoints. + """ + if value < 0: + raise ValueError('value must be >= 0') + + result = [] + for bit_index, name in enumerate(self.STATES): + if value & (1 << bit_index): + result.append(name) + + return result + + @property + def can_be_armed(self): + return self._bit(BIT_CAN_BE_ARMED) + + @property + def is_armed(self): + return self._bit(BIT_IS_ARMED) + + @property + def is_auto_armed(self): + return self._bit(BIT_IS_AUTO_ARMED) + + @property + def can_fly(self): + return self._bit(BIT_CAN_FLY) + + @property + def is_flying(self): + return self._bit(BIT_IS_FLYING) + + @property + def is_tumbled(self): + return self._bit(BIT_IS_TUMBLED) + + @property + def is_locked(self): + return self._bit(BIT_IS_LOCKED) + + @property + def is_crashed(self): + return self._bit(BIT_IS_CRASHED) + + @property + def hl_control_active(self): + return self._bit(BIT_HL_CONTROL_ACTIVE) + + @property + def hl_traj_finished(self): + return self._bit(BIT_HL_TRAJ_FINISHED) + + @property + def hl_control_disabled(self): + return self._bit(BIT_HL_CONTROL_DISABLED) diff --git a/cflib/crtp/crtpstack.py b/cflib/crtp/crtpstack.py index 2cb9118e7..8204764cd 100644 --- a/cflib/crtp/crtpstack.py +++ b/cflib/crtp/crtpstack.py @@ -47,6 +47,7 @@ class CRTPPort: LOCALIZATION = 0x06 COMMANDER_GENERIC = 0x07 SETPOINT_HL = 0x08 + SUPERVISOR = 0x09 PLATFORM = 0x0D LINKCTRL = 0x0F ALL = 0xFF From b28d0575a8ee016f2d09af368b612914fe1af230 Mon Sep 17 00:00:00 2001 From: aris Date: Wed, 19 Nov 2025 16:25:16 +0100 Subject: [PATCH 2/9] Added simple supervisor examples --- examples/supervisor/flying_with_supervisor.py | 89 +++++++++++++++++++ examples/supervisor/reading_supervisor.py | 63 +++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 examples/supervisor/flying_with_supervisor.py create mode 100644 examples/supervisor/reading_supervisor.py diff --git a/examples/supervisor/flying_with_supervisor.py b/examples/supervisor/flying_with_supervisor.py new file mode 100644 index 000000000..304fe6717 --- /dev/null +++ b/examples/supervisor/flying_with_supervisor.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# +# || ____ _ __ +# +------+ / __ )(_) /_______________ _____ ___ +# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ +# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ +# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ +# +# Copyright (C) 2025 Bitcraze AB +# +# Crazyflie Nano Quadcopter Client +# +# 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 2 +# 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 . +""" +Simple example showing how to fly the Crazyflie using supervisor state information. +Based on its current state, the Crazyflie will arm (if it can be armed), take +off (if it can fly), and land (if it is flying). Before each action, we call +the supervisor to check if the Crazyflie is crashed, locked or tumbled. +Tested with the Flow deck V2 and the Lighthouse positioning system. +""" +import time + +import cflib.crtp +from cflib.crazyflie import Crazyflie +from cflib.crazyflie.supervisor import SupervisorState +from cflib.crazyflie.syncCrazyflie import SyncCrazyflie +from cflib.utils import uri_helper + + +# URI to the Crazyflie to connect to +uri = uri_helper.uri_from_env(default='radio://0/30/2M/BADC0DE013') + + +def safety_check(sup: SupervisorState): + if sup.is_crashed: + raise Exception('Crazyflie crashed!') + if sup.is_locked: + raise Exception('Crazyflie locked!') + if sup.is_tumbled: + raise Exception('Crazyflie tumbled!') + + +def run_sequence(scf: SyncCrazyflie, sup: SupervisorState): + commander = scf.cf.high_level_commander + + try: + safety_check(sup) + if sup.can_be_armed: + print('The Crazyflie can be armed...arming!') + safety_check(sup) + scf.cf.platform.send_arming_request(True) + time.sleep(1) + + safety_check(sup) + if sup.can_fly: + print('The Crazyflie can fly...taking off!') + commander.takeoff(1.0, 2.0) + time.sleep(3) + + safety_check(sup) + if sup.is_flying: + print('The Crazyflie is flying...landing!') + commander.land(0.0, 2.0) + time.sleep(3) + + safety_check(sup) + + except Exception as e: + print(f'Safety check failed: {e}') + + +if __name__ == '__main__': + cflib.crtp.init_drivers() + + with SyncCrazyflie(uri, cf=Crazyflie(rw_cache='./cache')) as scf: + time.sleep(1) + supervisor = SupervisorState(scf) + time.sleep(1) + run_sequence(scf, supervisor) diff --git a/examples/supervisor/reading_supervisor.py b/examples/supervisor/reading_supervisor.py new file mode 100644 index 000000000..7dcbc46dc --- /dev/null +++ b/examples/supervisor/reading_supervisor.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# +# || ____ _ __ +# +------+ / __ )(_) /_______________ _____ ___ +# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ +# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ +# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ +# +# Copyright (C) 2025 Bitcraze AB +# +# Crazyflie Nano Quadcopter Client +# +# 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 2 +# 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 . +""" +Simple, non-flying example demonstrating how to read the Crazyflie's state +through the supervisor. Hold the Crazyflie in your hand and tilt it upside +down to observe the state changes. Once the tilt exceeds ~90°, the can_fly +state becomes False and the is_tumbled state becomes True. +""" +import logging +import time + +import cflib.crtp +from cflib.crazyflie import Crazyflie +from cflib.crazyflie.supervisor import SupervisorState +from cflib.crazyflie.syncCrazyflie import SyncCrazyflie +from cflib.utils import uri_helper + +logging.basicConfig(level=logging.INFO) + +# URI to the Crazyflie to connect to +uri = uri_helper.uri_from_env(default='radio://0/30/2M/BADC0DE010') + + +if __name__ == '__main__': + cflib.crtp.init_drivers() + + with SyncCrazyflie(uri, cf=Crazyflie(rw_cache='./cache')) as scf: + time.sleep(1) + supervisor = SupervisorState(scf) + time.sleep(1) + try: + while True: + print('==============================================================================') + print(f'Can fly: {supervisor.can_fly}') + print(f'Is tumbled: {supervisor.is_tumbled}') + print(f'Bitfield: {supervisor.read_bitfield()}') + print(f'State list: {supervisor.read_state_list()}') + print('==============================================================================') + time.sleep(0.5) + + except KeyboardInterrupt: + print('Script terminated') From 78732a29293b20606cfc00112fe43ce1993713b6 Mon Sep 17 00:00:00 2001 From: aris Date: Thu, 8 Jan 2026 11:50:07 +0100 Subject: [PATCH 3/9] Updated the uri adresses --- examples/supervisor/flying_with_supervisor.py | 2 +- examples/supervisor/reading_supervisor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/supervisor/flying_with_supervisor.py b/examples/supervisor/flying_with_supervisor.py index 304fe6717..769e88e51 100644 --- a/examples/supervisor/flying_with_supervisor.py +++ b/examples/supervisor/flying_with_supervisor.py @@ -38,7 +38,7 @@ # URI to the Crazyflie to connect to -uri = uri_helper.uri_from_env(default='radio://0/30/2M/BADC0DE013') +uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') def safety_check(sup: SupervisorState): diff --git a/examples/supervisor/reading_supervisor.py b/examples/supervisor/reading_supervisor.py index 7dcbc46dc..84e464b2c 100644 --- a/examples/supervisor/reading_supervisor.py +++ b/examples/supervisor/reading_supervisor.py @@ -39,7 +39,7 @@ logging.basicConfig(level=logging.INFO) # URI to the Crazyflie to connect to -uri = uri_helper.uri_from_env(default='radio://0/30/2M/BADC0DE010') +uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') if __name__ == '__main__': From 4716cd6890f0d7e6d7dc4a4ca228e459cc77faae Mon Sep 17 00:00:00 2001 From: aris Date: Thu, 8 Jan 2026 16:24:13 +0100 Subject: [PATCH 4/9] Added supervisor commands in supervisor.py --- cflib/crazyflie/supervisor.py | 53 +++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/cflib/crazyflie/supervisor.py b/cflib/crazyflie/supervisor.py index 08d15353d..1aa22355b 100644 --- a/cflib/crazyflie/supervisor.py +++ b/cflib/crazyflie/supervisor.py @@ -20,20 +20,22 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ -Provides access to the supervisor modue of the Crazyflie platform. +Provides access to the supervisor module of the Crazyflie platform. """ import logging import time -from cflib.crazyflie.syncCrazyflie import SyncCrazyflie from cflib.crtp.crtpstack import CRTPPacket from cflib.crtp.crtpstack import CRTPPort __author__ = 'Bitcraze AB' -__all__ = ['SupervisorState'] +__all__ = ['Supervisor'] logger = logging.getLogger(__name__) +SUPERVISOR_CH_INFO = 0 +SUPERVISOR_CH_COMMAND = 1 + # Bit positions BIT_CAN_BE_ARMED = 0 BIT_IS_ARMED = 1 @@ -48,14 +50,21 @@ BIT_HL_CONTROL_DISABLED = 10 CMD_GET_STATE_BITFIELD = 0x0C -SUPERVISOR_CH_INFO = 0 +CMD_ARM_SYSTEM = 0x01 +CMD_RECOVER_SYSTEM = 0x02 -class SupervisorState: + +class Supervisor: """ - This class is used to easily get the state of your Crazyflie through - the supervisor. Check the `reading_supervisor.py` example in - examples/supervisor/ to better understand how to use it. + This class provides two main functionalities: + + 1. **Reading the system state** + Accesses the Crazyflie's supervisor bitfield to determine the current state, + such as whether it can be armed, is flying or crashed. + + 2. **Sending system commands** + Sends arming/disarming requests and crash recovery commands to the Crazyflie. """ STATES = [ 'Can be armed', @@ -72,10 +81,7 @@ class SupervisorState: ] def __init__(self, crazyflie): - if isinstance(crazyflie, SyncCrazyflie): - self._cf = crazyflie.cf - else: - self._cf = crazyflie + self._cf = crazyflie self._cache_timeout = 0.1 # seconds self._last_fetch_time = 0 @@ -216,3 +222,26 @@ def hl_traj_finished(self): @property def hl_control_disabled(self): return self._bit(BIT_HL_CONTROL_DISABLED) + + def send_arming_request(self, do_arm: bool): + """ + Send system arm/disarm request. + + Args: + do_arm (bool): True = arm the system, False = disarm the system + """ + pk = CRTPPacket() + pk.set_header(CRTPPort.SUPERVISOR, SUPERVISOR_CH_COMMAND) + pk.data = (CMD_ARM_SYSTEM, do_arm) + self._cf.send_packet(pk) + logger.debug(f"Sent arming request: do_arm={do_arm}") + + def send_crash_recovery_request(self): + """ + Send crash recovery request. + """ + pk = CRTPPacket() + pk.set_header(CRTPPort.SUPERVISOR, SUPERVISOR_CH_COMMAND) + pk.data = (CMD_RECOVER_SYSTEM,) + self._cf.send_packet(pk) + logger.debug('Sent crash recovery request') From 7c314be474c4e359a1cf50e68b49c5877520f3d4 Mon Sep 17 00:00:00 2001 From: aris Date: Thu, 8 Jan 2026 16:25:20 +0100 Subject: [PATCH 5/9] Updated crazyflie/__init__.py --- cflib/crazyflie/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cflib/crazyflie/__init__.py b/cflib/crazyflie/__init__.py index 5e09b906d..89bfedf1c 100644 --- a/cflib/crazyflie/__init__.py +++ b/cflib/crazyflie/__init__.py @@ -51,6 +51,7 @@ from .mem import Memory from .param import Param from .platformservice import PlatformService +from .supervisor import Supervisor from .toccache import TocCache from cflib.crazyflie.high_level_commander import HighLevelCommander from cflib.utils.callbacks import Caller @@ -124,6 +125,7 @@ def __init__(self, link=None, ro_cache=None, rw_cache=None): self.platform = PlatformService(self) self.appchannel = Appchannel(self) self.link_statistics = LinkStatistics(self) + self.supervisor = Supervisor(self) self.link_uri = '' From d5b00622c12af5de372a082e84c018669e68d1be Mon Sep 17 00:00:00 2001 From: aris Date: Thu, 8 Jan 2026 16:26:23 +0100 Subject: [PATCH 6/9] Added deprecation warnings in platformservice.py --- cflib/crazyflie/platformservice.py | 36 ++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/cflib/crazyflie/platformservice.py b/cflib/crazyflie/platformservice.py index 262587a25..527fef2a2 100644 --- a/cflib/crazyflie/platformservice.py +++ b/cflib/crazyflie/platformservice.py @@ -26,6 +26,7 @@ Used for sending control setpoints to the Crazyflie """ import logging +import warnings from cflib.crtp.crtpstack import CRTPPacket from cflib.crtp.crtpstack import CRTPPort @@ -40,8 +41,6 @@ APP_CHANNEL = 2 PLATFORM_SET_CONT_WAVE = 0 -PLATFORM_REQUEST_ARMING = 1 -PLATFORM_REQUEST_CRASH_RECOVERY = 2 VERSION_GET_PROTOCOL = 0 VERSION_GET_FIRMWARE = 1 @@ -90,25 +89,38 @@ def set_continous_wave(self, enabled): def send_arming_request(self, do_arm: bool): """ - Send system arm/disarm request + Send system arm/disarm request (deprecated). Args: do_arm (bool): True = arm the system, False = disarm the system + + Deprecated: + Use `supervisor.send_arming_request(do_arm)` instead. """ - pk = CRTPPacket() - pk.set_header(CRTPPort.PLATFORM, PLATFORM_COMMAND) - pk.data = (PLATFORM_REQUEST_ARMING, do_arm) - self._cf.send_packet(pk) + warnings.warn( + 'platform.send_arming_request is deprecated. ' + 'Use supervisor.send_arming_request(do_arm) instead.', + category=DeprecationWarning, + stacklevel=2 + ) + + self._cf.supervisor.send_arming_request(do_arm) def send_crash_recovery_request(self): """ - Send crash recovery request + Send crash recovery request (deprecated). + Deprecated: + Use `supervisor.send_crash_recovery_request()` instead. """ - pk = CRTPPacket() - pk.set_header(CRTPPort.PLATFORM, PLATFORM_COMMAND) - pk.data = (PLATFORM_REQUEST_CRASH_RECOVERY, ) - self._cf.send_packet(pk) + warnings.warn( + 'platform.send_crash_recovery_request is deprecated. ' + 'Use supervisor.send_crash_recovery_request() instead.', + category=DeprecationWarning, + stacklevel=2 + ) + + self._cf.supervisor.send_crash_recovery_request() def get_protocol_version(self): """ From d03c5ff839b1dda8b70f63ece9e5c470745a109b Mon Sep 17 00:00:00 2001 From: aris Date: Thu, 8 Jan 2026 16:31:29 +0100 Subject: [PATCH 7/9] Updated the supervisor examples --- examples/supervisor/flying_with_supervisor.py | 9 ++++----- examples/supervisor/reading_supervisor.py | 3 +-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/examples/supervisor/flying_with_supervisor.py b/examples/supervisor/flying_with_supervisor.py index 769e88e51..d6b9432d8 100644 --- a/examples/supervisor/flying_with_supervisor.py +++ b/examples/supervisor/flying_with_supervisor.py @@ -32,7 +32,6 @@ import cflib.crtp from cflib.crazyflie import Crazyflie -from cflib.crazyflie.supervisor import SupervisorState from cflib.crazyflie.syncCrazyflie import SyncCrazyflie from cflib.utils import uri_helper @@ -41,7 +40,7 @@ uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') -def safety_check(sup: SupervisorState): +def safety_check(sup): if sup.is_crashed: raise Exception('Crazyflie crashed!') if sup.is_locked: @@ -50,7 +49,7 @@ def safety_check(sup: SupervisorState): raise Exception('Crazyflie tumbled!') -def run_sequence(scf: SyncCrazyflie, sup: SupervisorState): +def run_sequence(scf: SyncCrazyflie, sup): commander = scf.cf.high_level_commander try: @@ -58,7 +57,7 @@ def run_sequence(scf: SyncCrazyflie, sup: SupervisorState): if sup.can_be_armed: print('The Crazyflie can be armed...arming!') safety_check(sup) - scf.cf.platform.send_arming_request(True) + sup.send_arming_request(True) time.sleep(1) safety_check(sup) @@ -84,6 +83,6 @@ def run_sequence(scf: SyncCrazyflie, sup: SupervisorState): with SyncCrazyflie(uri, cf=Crazyflie(rw_cache='./cache')) as scf: time.sleep(1) - supervisor = SupervisorState(scf) + supervisor = scf.cf.supervisor time.sleep(1) run_sequence(scf, supervisor) diff --git a/examples/supervisor/reading_supervisor.py b/examples/supervisor/reading_supervisor.py index 84e464b2c..edbe68e5f 100644 --- a/examples/supervisor/reading_supervisor.py +++ b/examples/supervisor/reading_supervisor.py @@ -32,7 +32,6 @@ import cflib.crtp from cflib.crazyflie import Crazyflie -from cflib.crazyflie.supervisor import SupervisorState from cflib.crazyflie.syncCrazyflie import SyncCrazyflie from cflib.utils import uri_helper @@ -47,7 +46,7 @@ with SyncCrazyflie(uri, cf=Crazyflie(rw_cache='./cache')) as scf: time.sleep(1) - supervisor = SupervisorState(scf) + supervisor = scf.cf.supervisor time.sleep(1) try: while True: From 6e0af910bce186781df4e3d2b9e590aedc936878 Mon Sep 17 00:00:00 2001 From: aris Date: Mon, 12 Jan 2026 16:02:36 +0100 Subject: [PATCH 8/9] Updated supervisor callback to include the commands --- cflib/crazyflie/supervisor.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/cflib/crazyflie/supervisor.py b/cflib/crazyflie/supervisor.py index 1aa22355b..c7241f03b 100644 --- a/cflib/crazyflie/supervisor.py +++ b/cflib/crazyflie/supervisor.py @@ -87,7 +87,7 @@ def __init__(self, crazyflie): self._last_fetch_time = 0 self._bitfield = None self._cf.add_port_callback(CRTPPort.SUPERVISOR, self._supervisor_callback) - self._response_received = False + self._bitfield_response_received = False def _supervisor_callback(self, pk: CRTPPacket): """ @@ -101,9 +101,19 @@ def _supervisor_callback(self, pk: CRTPPacket): orig_cmd = cmd & 0x7F if orig_cmd == CMD_GET_STATE_BITFIELD: self._bitfield = int.from_bytes(pk.data[1:], byteorder='little') - self._response_received = True + self._bitfield_response_received = True logger.info(f'Supervisor bitfield received: 0x{self._bitfield:04X}') + elif orig_cmd == CMD_ARM_SYSTEM: + success = bool(pk.data[1]) + is_armed = bool(pk.data[2]) + logger.info(f'Arm response: success={success}, is_armed={is_armed}') + + elif orig_cmd == CMD_RECOVER_SYSTEM: + success = bool(pk.data[1]) + recovered = bool(pk.data[2]) + logger.info(f'Recovery response: success={success}, recovered={recovered}') + def _fetch_bitfield(self, timeout=0.2): """ Request the bitfield and wait for response (blocking). @@ -116,7 +126,7 @@ def _fetch_bitfield(self, timeout=0.2): return self._bitfield # Send a new request - self._response_received = False + self._bitfield_response_received = False pk = CRTPPacket() pk.set_header(CRTPPort.SUPERVISOR, SUPERVISOR_CH_INFO) pk.data = [CMD_GET_STATE_BITFIELD] @@ -124,7 +134,7 @@ def _fetch_bitfield(self, timeout=0.2): # Wait for response start_time = now - while not self._response_received: + while not self._bitfield_response_received: if time.time() - start_time > timeout: logger.warning('Timeout waiting for supervisor bitfield response') return self._bitfield or 0 # still return last known value From 1dd7c811ef69a56bfc6d669d8d660f93cf8e3ae4 Mon Sep 17 00:00:00 2001 From: aris Date: Mon, 12 Jan 2026 16:38:53 +0100 Subject: [PATCH 9/9] Fixed double-quote failing CI --- cflib/crazyflie/supervisor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cflib/crazyflie/supervisor.py b/cflib/crazyflie/supervisor.py index c7241f03b..2fea7c352 100644 --- a/cflib/crazyflie/supervisor.py +++ b/cflib/crazyflie/supervisor.py @@ -244,7 +244,7 @@ def send_arming_request(self, do_arm: bool): pk.set_header(CRTPPort.SUPERVISOR, SUPERVISOR_CH_COMMAND) pk.data = (CMD_ARM_SYSTEM, do_arm) self._cf.send_packet(pk) - logger.debug(f"Sent arming request: do_arm={do_arm}") + logger.debug(f'Sent arming request: do_arm={do_arm}') def send_crash_recovery_request(self): """