From 968159f7cf9f6bea665c058e37ff186a82297ebb Mon Sep 17 00:00:00 2001 From: Rik Bouwmeester Date: Thu, 9 Apr 2026 11:50:18 +0200 Subject: [PATCH 1/3] Add version-gated fallback for deprecated arming/recovery wrappers The deprecated platform.send_arming_request and platform.send_crash_recovery_request delegated to the supervisor module which version-gates on protocol >= 12, causing commands to be silently dropped against old firmware. Add fallback to the old platform port protocol, matching the pattern used in localization.send_emergency_stop. --- cflib/crazyflie/platformservice.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/cflib/crazyflie/platformservice.py b/cflib/crazyflie/platformservice.py index 527fef2a2..09c40bdfe 100644 --- a/cflib/crazyflie/platformservice.py +++ b/cflib/crazyflie/platformservice.py @@ -96,6 +96,8 @@ def send_arming_request(self, do_arm: bool): Deprecated: Use `supervisor.send_arming_request(do_arm)` instead. + If the connected Crazyflie does not support CRTP protocol version 12 + or later, the legacy platform channel is used as a fallback. """ warnings.warn( 'platform.send_arming_request is deprecated. ' @@ -104,7 +106,13 @@ def send_arming_request(self, do_arm: bool): stacklevel=2 ) - self._cf.supervisor.send_arming_request(do_arm) + if self._cf.platform.get_protocol_version() >= 12: + self._cf.supervisor.send_arming_request(do_arm) + else: + pk = CRTPPacket() + pk.set_header(CRTPPort.PLATFORM, PLATFORM_COMMAND) + pk.data = (0x01, do_arm) + self._cf.send_packet(pk) def send_crash_recovery_request(self): """ @@ -112,6 +120,8 @@ def send_crash_recovery_request(self): Deprecated: Use `supervisor.send_crash_recovery_request()` instead. + If the connected Crazyflie does not support CRTP protocol version 12 + or later, the legacy platform channel is used as a fallback. """ warnings.warn( 'platform.send_crash_recovery_request is deprecated. ' @@ -120,7 +130,13 @@ def send_crash_recovery_request(self): stacklevel=2 ) - self._cf.supervisor.send_crash_recovery_request() + if self._cf.platform.get_protocol_version() >= 12: + self._cf.supervisor.send_crash_recovery_request() + else: + pk = CRTPPacket() + pk.set_header(CRTPPort.PLATFORM, PLATFORM_COMMAND) + pk.data = (0x02,) + self._cf.send_packet(pk) def get_protocol_version(self): """ From dde90941ca330be33b0e63ad8086ee1f0586f716 Mon Sep 17 00:00:00 2001 From: Rik Bouwmeester Date: Thu, 9 Apr 2026 12:03:42 +0200 Subject: [PATCH 2/3] Add legacy fallback in supervisor for old firmware Supervisor methods now fall back to legacy CRTP ports when connected to firmware with protocol < 12. Arming/recovery falls back to the platform port, emergency stop to the localization port. A warning is emitted in all fallback cases. Reverts platformservice.py wrappers back to simple delegation since supervisor.py now handles the fallback itself. --- cflib/crazyflie/platformservice.py | 20 +----- cflib/crazyflie/supervisor.py | 109 +++++++++++++++++++---------- 2 files changed, 75 insertions(+), 54 deletions(-) diff --git a/cflib/crazyflie/platformservice.py b/cflib/crazyflie/platformservice.py index 09c40bdfe..527fef2a2 100644 --- a/cflib/crazyflie/platformservice.py +++ b/cflib/crazyflie/platformservice.py @@ -96,8 +96,6 @@ def send_arming_request(self, do_arm: bool): Deprecated: Use `supervisor.send_arming_request(do_arm)` instead. - If the connected Crazyflie does not support CRTP protocol version 12 - or later, the legacy platform channel is used as a fallback. """ warnings.warn( 'platform.send_arming_request is deprecated. ' @@ -106,13 +104,7 @@ def send_arming_request(self, do_arm: bool): stacklevel=2 ) - if self._cf.platform.get_protocol_version() >= 12: - self._cf.supervisor.send_arming_request(do_arm) - else: - pk = CRTPPacket() - pk.set_header(CRTPPort.PLATFORM, PLATFORM_COMMAND) - pk.data = (0x01, do_arm) - self._cf.send_packet(pk) + self._cf.supervisor.send_arming_request(do_arm) def send_crash_recovery_request(self): """ @@ -120,8 +112,6 @@ def send_crash_recovery_request(self): Deprecated: Use `supervisor.send_crash_recovery_request()` instead. - If the connected Crazyflie does not support CRTP protocol version 12 - or later, the legacy platform channel is used as a fallback. """ warnings.warn( 'platform.send_crash_recovery_request is deprecated. ' @@ -130,13 +120,7 @@ def send_crash_recovery_request(self): stacklevel=2 ) - if self._cf.platform.get_protocol_version() >= 12: - self._cf.supervisor.send_crash_recovery_request() - else: - pk = CRTPPacket() - pk.set_header(CRTPPort.PLATFORM, PLATFORM_COMMAND) - pk.data = (0x02,) - self._cf.send_packet(pk) + self._cf.supervisor.send_crash_recovery_request() def get_protocol_version(self): """ diff --git a/cflib/crazyflie/supervisor.py b/cflib/crazyflie/supervisor.py index 826577ed5..b12061194 100644 --- a/cflib/crazyflie/supervisor.py +++ b/cflib/crazyflie/supervisor.py @@ -23,6 +23,7 @@ Provides access to the supervisor module of the Crazyflie platform. """ import logging +import struct import threading import time import warnings @@ -93,18 +94,19 @@ def __init__(self, crazyflie): self._cf.add_port_callback(CRTPPort.SUPERVISOR, self._supervisor_callback) self._bitfield_received = threading.Event() - def _check_protocol_version(self): - """Returns True if the protocol version is supported, False otherwise.""" + def _is_legacy_firmware(self): + """Returns True if the firmware does not support the supervisor port.""" + return self._cf.platform.get_protocol_version() < 12 + + def _warn_legacy_firmware(self): + """Warn that the firmware is too old for the supervisor port.""" version = self._cf.platform.get_protocol_version() - if version < 12: - warnings.warn( - 'The supervisor subsystem requires CRTP protocol version 12 or later. ' - f'Connected Crazyflie reports version {version}. ' - 'Update your Crazyflie firmware.', - stacklevel=3, - ) - return False - return True + warnings.warn( + 'The supervisor subsystem requires CRTP protocol version 12 or later. ' + f'Connected Crazyflie reports version {version}. ' + 'Update your Crazyflie firmware. Using legacy fallback.', + stacklevel=3, + ) def _supervisor_callback(self, pk: CRTPPacket): """ @@ -136,7 +138,8 @@ 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. """ - if not self._check_protocol_version(): + if self._is_legacy_firmware(): + self._warn_legacy_firmware() return 0 now = time.time() @@ -253,50 +256,84 @@ def send_arming_request(self, do_arm: bool): """ Send system arm/disarm request. + If the connected Crazyflie does not support CRTP protocol version 12 + or later, the legacy platform channel is used as a fallback. + Args: do_arm (bool): True = arm the system, False = disarm the system """ - if not self._check_protocol_version(): - return - pk = CRTPPacket() - pk.set_header(CRTPPort.SUPERVISOR, SUPERVISOR_CH_COMMAND) - pk.data = (CMD_ARM_SYSTEM, do_arm) - self._cf.send_packet(pk) + if self._is_legacy_firmware(): + self._warn_legacy_firmware() + pk = CRTPPacket() + pk.set_header(CRTPPort.PLATFORM, 0) + pk.data = (0x01, do_arm) + self._cf.send_packet(pk) + else: + 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. + + If the connected Crazyflie does not support CRTP protocol version 12 + or later, the legacy platform channel is used as a fallback. """ - if not self._check_protocol_version(): - return - pk = CRTPPacket() - pk.set_header(CRTPPort.SUPERVISOR, SUPERVISOR_CH_COMMAND) - pk.data = (CMD_RECOVER_SYSTEM,) - self._cf.send_packet(pk) + if self._is_legacy_firmware(): + self._warn_legacy_firmware() + pk = CRTPPacket() + pk.set_header(CRTPPort.PLATFORM, 0) + pk.data = (0x02,) + self._cf.send_packet(pk) + else: + 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') def send_emergency_stop(self): """ Send emergency stop. The Crazyflie will immediately stop all motors. + + If the connected Crazyflie does not support CRTP protocol version 12 + or later, the legacy localization channel is used as a fallback. """ - if not self._check_protocol_version(): - return - pk = CRTPPacket() - pk.set_header(CRTPPort.SUPERVISOR, SUPERVISOR_CH_COMMAND) - pk.data = (CMD_EMERGENCY_STOP,) - self._cf.send_packet(pk) + if self._is_legacy_firmware(): + self._warn_legacy_firmware() + pk = CRTPPacket() + pk.port = CRTPPort.LOCALIZATION + pk.channel = 1 + pk.data = struct.pack(' Date: Thu, 9 Apr 2026 13:12:36 +0200 Subject: [PATCH 3/3] Replace magic numbers in legacy fallbacks with named constants --- cflib/crazyflie/platformservice.py | 2 ++ cflib/crazyflie/supervisor.py | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/cflib/crazyflie/platformservice.py b/cflib/crazyflie/platformservice.py index 527fef2a2..3055095a5 100644 --- a/cflib/crazyflie/platformservice.py +++ b/cflib/crazyflie/platformservice.py @@ -41,6 +41,8 @@ APP_CHANNEL = 2 PLATFORM_SET_CONT_WAVE = 0 +PLATFORM_REQUEST_ARMING = 1 # Deprecated: use supervisor.send_arming_request() +PLATFORM_REQUEST_CRASH_RECOVERY = 2 # Deprecated: use supervisor.send_crash_recovery_request() VERSION_GET_PROTOCOL = 0 VERSION_GET_FIRMWARE = 1 diff --git a/cflib/crazyflie/supervisor.py b/cflib/crazyflie/supervisor.py index b12061194..08249871e 100644 --- a/cflib/crazyflie/supervisor.py +++ b/cflib/crazyflie/supervisor.py @@ -28,6 +28,10 @@ import time import warnings +from cflib.crazyflie.localization import Localization +from cflib.crazyflie.platformservice import PLATFORM_COMMAND +from cflib.crazyflie.platformservice import PLATFORM_REQUEST_ARMING +from cflib.crazyflie.platformservice import PLATFORM_REQUEST_CRASH_RECOVERY from cflib.crtp.crtpstack import CRTPPacket from cflib.crtp.crtpstack import CRTPPort @@ -265,8 +269,8 @@ def send_arming_request(self, do_arm: bool): if self._is_legacy_firmware(): self._warn_legacy_firmware() pk = CRTPPacket() - pk.set_header(CRTPPort.PLATFORM, 0) - pk.data = (0x01, do_arm) + pk.set_header(CRTPPort.PLATFORM, PLATFORM_COMMAND) + pk.data = (PLATFORM_REQUEST_ARMING, do_arm) self._cf.send_packet(pk) else: pk = CRTPPacket() @@ -285,8 +289,8 @@ def send_crash_recovery_request(self): if self._is_legacy_firmware(): self._warn_legacy_firmware() pk = CRTPPacket() - pk.set_header(CRTPPort.PLATFORM, 0) - pk.data = (0x02,) + pk.set_header(CRTPPort.PLATFORM, PLATFORM_COMMAND) + pk.data = (PLATFORM_REQUEST_CRASH_RECOVERY,) self._cf.send_packet(pk) else: pk = CRTPPacket() @@ -305,9 +309,8 @@ def send_emergency_stop(self): if self._is_legacy_firmware(): self._warn_legacy_firmware() pk = CRTPPacket() - pk.port = CRTPPort.LOCALIZATION - pk.channel = 1 - pk.data = struct.pack('