Skip to content

Commit 57fd360

Browse files
committed
Added bindings for the led_driver memory
1 parent 526d0ab commit 57fd360

5 files changed

Lines changed: 202 additions & 4 deletions

File tree

cflib2/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from cflib2._rust import (
2626
Crazyflie,
2727
LinkContext,
28+
LedRingColor,
2829
# TOC cache classes (passed to Crazyflie.connect_from_uri)
2930
NoTocCache,
3031
InMemoryTocCache,
@@ -50,6 +51,7 @@
5051
__all__ = [
5152
"Crazyflie",
5253
"LinkContext",
54+
"LedRingColor",
5355
"NoTocCache",
5456
"InMemoryTocCache",
5557
"FileTocCache",

cflib2/_rust.pyi

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,86 @@ class InvalidParameterError(CrazyflieError):
705705

706706
...
707707

708+
@typing.final
709+
class LedRingColor:
710+
r"""
711+
A single LED color and intensity for the Crazyflie LED ring.
712+
713+
Used to build the list of 12 LED values passed to `Memory.write_led_ring()`.
714+
"""
715+
@property
716+
def r(self) -> builtins.int:
717+
r"""
718+
Red component (0-255)
719+
"""
720+
@r.setter
721+
def r(self, value: builtins.int) -> None:
722+
r"""
723+
Red component (0-255)
724+
"""
725+
@property
726+
def g(self) -> builtins.int:
727+
r"""
728+
Green component (0-255)
729+
"""
730+
@g.setter
731+
def g(self, value: builtins.int) -> None:
732+
r"""
733+
Green component (0-255)
734+
"""
735+
@property
736+
def b(self) -> builtins.int:
737+
r"""
738+
Blue component (0-255)
739+
"""
740+
@b.setter
741+
def b(self, value: builtins.int) -> None:
742+
r"""
743+
Blue component (0-255)
744+
"""
745+
@property
746+
def intensity(self) -> builtins.int:
747+
r"""
748+
Intensity percentage (0-100); values above 100 are clamped to 100
749+
"""
750+
@intensity.setter
751+
def intensity(self, value: builtins.int) -> None:
752+
r"""
753+
Intensity percentage (0-100); values above 100 are clamped to 100
754+
"""
755+
def __new__(
756+
cls,
757+
r: builtins.int = 0,
758+
g: builtins.int = 0,
759+
b: builtins.int = 0,
760+
intensity: builtins.int = 100,
761+
) -> LedRingColor:
762+
r"""
763+
Create a new LedRingColor.
764+
765+
# Arguments
766+
* `r` - Red component (0-255, default 0)
767+
* `g` - Green component (0-255, default 0)
768+
* `b` - Blue component (0-255, default 0)
769+
* `intensity` - Intensity percentage (0-100, default 100); clamped to 100 if higher
770+
"""
771+
def set(
772+
self,
773+
r: builtins.int,
774+
g: builtins.int,
775+
b: builtins.int,
776+
intensity: typing.Optional[builtins.int] = None,
777+
) -> None:
778+
r"""
779+
Set R/G/B and optionally intensity in one call.
780+
781+
# Arguments
782+
* `r` - Red component (0-255)
783+
* `g` - Green component (0-255)
784+
* `b` - Blue component (0-255)
785+
* `intensity` - Intensity percentage (0-100); if None, keeps current value; clamped to 100 if higher
786+
"""
787+
708788
@typing.final
709789
class Lighthouse:
710790
r"""
@@ -1047,6 +1127,16 @@ class Memory:
10471127
* `segments` - List of CompressedSegment instances
10481128
* `start_addr` - Address in trajectory memory (default 0)
10491129
"""
1130+
async def write_led_ring(self, leds: typing.Sequence[LedRingColor]) -> None:
1131+
r"""
1132+
Write LED colors to the Crazyflie LED ring.
1133+
1134+
Opens the LED driver memory, sets all 12 LED values, writes them to
1135+
the ring, and closes the memory.
1136+
1137+
# Arguments
1138+
* `leds` - List of exactly 12 LedRingColor instances
1139+
"""
10501140
def get_memories(
10511141
self, memory_type: typing.Optional[builtins.int] = None
10521142
) -> builtins.list[tuple[builtins.int, builtins.int, builtins.int]]:

rust/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use subsystems::{
3838
Commander, Console, Log, LogBlock, LogData, LogStream, Param, PersistentParamState, Platform, AppChannel,
3939
Localization, EmergencyControl, ExternalPose, Lighthouse, LocoPositioning,
4040
LighthouseAngleData, LighthouseAngles,
41-
Memory, Poly, Poly4D, CompressedStart, CompressedSegment,
41+
Memory, Poly, Poly4D, CompressedStart, CompressedSegment, LedRingColor,
4242
};
4343
use toc_cache::{NoTocCache, InMemoryTocCache, FileTocCache};
4444

@@ -69,6 +69,7 @@ fn _rust(m: &Bound<'_, PyModule>) -> PyResult<()> {
6969
m.add_class::<Poly4D>()?;
7070
m.add_class::<CompressedStart>()?;
7171
m.add_class::<CompressedSegment>()?;
72+
m.add_class::<LedRingColor>()?;
7273
m.add_class::<NoTocCache>()?;
7374
m.add_class::<InMemoryTocCache>()?;
7475
m.add_class::<FileTocCache>()?;

rust/src/subsystems/memory.rs

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121

2222
//! # Memory subsystem bindings
2323
//!
24-
//! Provides Python bindings for trajectory memory operations.
24+
//! Provides Python bindings for memory operations.
2525
//! Trajectory data is built in Python using [`Poly`], [`Poly4D`],
2626
//! [`CompressedStart`], and [`CompressedSegment`], then uploaded
27-
//! via the [`Memory`] subsystem.
27+
//! via the [`Memory`] subsystem. LED ring colors are set using
28+
//! [`LedRingColor`] and written via [`Memory::write_led_ring`].
2829
2930
use pyo3::prelude::*;
3031
use pyo3::exceptions::PyValueError;
@@ -34,6 +35,61 @@ use std::sync::Arc;
3435
use crate::error::to_pyerr;
3536
use crazyflie_lib::subsystems::memory::MemoryType;
3637

38+
/// A single LED color and intensity for the Crazyflie LED ring.
39+
///
40+
/// Used to build the list of 12 LED values passed to `Memory.write_led_ring()`.
41+
#[gen_stub_pyclass]
42+
#[pyclass]
43+
#[derive(Clone, Debug)]
44+
pub struct LedRingColor {
45+
/// Red component (0-255)
46+
#[pyo3(get, set)]
47+
r: u8,
48+
/// Green component (0-255)
49+
#[pyo3(get, set)]
50+
g: u8,
51+
/// Blue component (0-255)
52+
#[pyo3(get, set)]
53+
b: u8,
54+
/// Intensity percentage (0-100); values above 100 are clamped to 100
55+
#[pyo3(get, set)]
56+
intensity: u8,
57+
}
58+
59+
#[gen_stub_pymethods]
60+
#[pymethods]
61+
impl LedRingColor {
62+
/// Create a new LedRingColor.
63+
///
64+
/// # Arguments
65+
/// * `r` - Red component (0-255, default 0)
66+
/// * `g` - Green component (0-255, default 0)
67+
/// * `b` - Blue component (0-255, default 0)
68+
/// * `intensity` - Intensity percentage (0-100, default 100); clamped to 100 if higher
69+
#[new]
70+
#[pyo3(signature = (r=0, g=0, b=0, intensity=100))]
71+
fn new(r: u8, g: u8, b: u8, intensity: u8) -> Self {
72+
Self { r, g, b, intensity: intensity.min(100) }
73+
}
74+
75+
/// Set R/G/B and optionally intensity in one call.
76+
///
77+
/// # Arguments
78+
/// * `r` - Red component (0-255)
79+
/// * `g` - Green component (0-255)
80+
/// * `b` - Blue component (0-255)
81+
/// * `intensity` - Intensity percentage (0-100); if None, keeps current value; clamped to 100 if higher
82+
#[pyo3(signature = (r, g, b, intensity=None))]
83+
fn set(&mut self, r: u8, g: u8, b: u8, intensity: Option<u8>) {
84+
self.r = r;
85+
self.g = g;
86+
self.b = b;
87+
if let Some(i) = intensity {
88+
self.intensity = i.min(100);
89+
}
90+
}
91+
}
92+
3793
/// A polynomial with up to 8 coefficients.
3894
///
3995
/// Coefficients beyond the provided values are zero-filled.
@@ -352,6 +408,55 @@ impl Memory {
352408
})
353409
}
354410

411+
/// Write LED colors to the Crazyflie LED ring.
412+
///
413+
/// Opens the LED driver memory, sets all 12 LED values, writes them to
414+
/// the ring, and closes the memory.
415+
///
416+
/// # Arguments
417+
/// * `leds` - List of exactly 12 LedRingColor instances
418+
#[gen_stub(override_return_type(type_repr = "collections.abc.Coroutine[typing.Any, typing.Any, None]"))]
419+
fn write_led_ring<'py>(
420+
&self,
421+
py: Python<'py>,
422+
leds: Vec<LedRingColor>,
423+
) -> PyResult<Bound<'py, PyAny>> {
424+
let cf = self.cf.clone();
425+
pyo3_async_runtimes::tokio::future_into_py(py, async move {
426+
if leds.len() != 12 {
427+
return Err(PyValueError::new_err(
428+
format!("Expected 12 LEDs, got {}", leds.len())
429+
));
430+
}
431+
432+
let memories = cf.memory.get_memories(Some(MemoryType::DriverLed));
433+
let mem_device = (*memories.first()
434+
.ok_or_else(|| to_pyerr(crazyflie_lib::Error::MemoryError(
435+
"No LED driver memory found on Crazyflie".to_owned()
436+
)))?)
437+
.clone();
438+
439+
let mut led_mem: crazyflie_lib::subsystems::memory::LedDriverMemory =
440+
cf.memory.initialize_memory(mem_device).await
441+
.ok_or_else(|| to_pyerr(crazyflie_lib::Error::MemoryError(
442+
"Failed to open LED driver memory".to_owned()
443+
)))?
444+
.map_err(to_pyerr)?;
445+
446+
for (i, led) in leds.iter().enumerate() {
447+
led_mem.leds[i].r = led.r;
448+
led_mem.leds[i].g = led.g;
449+
led_mem.leds[i].b = led.b;
450+
led_mem.leds[i].intensity = led.intensity;
451+
}
452+
453+
led_mem.write_leds().await.map_err(to_pyerr)?;
454+
cf.memory.close_memory(led_mem).await.map_err(to_pyerr)?;
455+
456+
Ok(())
457+
})
458+
}
459+
355460
/// List all memories available on the Crazyflie.
356461
///
357462
/// Returns a list of tuples `(id, type, size)`:

rust/src/subsystems/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ pub use console::Console;
3535
pub use high_level_commander::HighLevelCommander;
3636
pub use localization::{Localization, EmergencyControl, ExternalPose, Lighthouse, LocoPositioning, LighthouseAngleData, LighthouseAngles};
3737
pub use log::{Log, LogBlock, LogData, LogStream};
38-
pub use memory::{Memory, Poly, Poly4D, CompressedStart, CompressedSegment};
38+
pub use memory::{Memory, Poly, Poly4D, CompressedStart, CompressedSegment, LedRingColor};
3939
pub use param::{Param, PersistentParamState};
4040
pub use platform::{Platform, AppChannel};

0 commit comments

Comments
 (0)